Unions and Interfaces

Similar to most type systems, GraphQL knows abstract types. There are two kinds of abstract types: Interfaces and Unions

Interfaces

An interface type can be used for abstract types that share fields.

SDL
interface Message {
sendBy: User!
createdAt: DateTime!
}

An object type or interface type that implements an interface, does have to declare all the fields that are declared on the interface.

SDL
type TextMessage implements Message {
sendBy: User!
createdAt: DateTime!
content: String!
}
interface MediaMessage implements Message {
sendBy: User!
createdAt: DateTime!
mediaType: MediaType!
}
type VideoMessage implements MediaMessage {
sendBy: User!
createdAt: DateTime!
mediaType: MediaType!
videoUrl: String!
}

A type can also implement multiple interfaces.

SDL
type VideoMessage implements Message & HasMediaType {
sendBy: User!
createdAt: DateTime!
mediaType: MediaType!
videoUrl: String!
}

Querying Interfaces

All fields declared on the interface type are available to query directly. Inline Fragments 📄 allow to query for fields of a specific implementation.

GraphQL
{
messages {
__typename
sendBy {
userName
}
createdAt
... on TextMessage {
content
}
... on VideoMessage {
videoUrl
}
... on MediaMessage {
mediaType
}
}
}
JSON
{
"messages": [
{
"__typename": "TextMessage",
"sendBy": {
"userName": "CookingMaster86"
},
"createdAt": "2020-01-01T11:43:00Z",
"context": "Hi there, can you show me how you did it?"
}
{
"__typename": "VideoMessage",
"sendBy": {
"userName": "SpicyChicken404"
},
"createdAt": "2020-01-01T12:00:00Z",
"videoUrl": "http://chillicream.com/cooking/recipies",
}
]
}

Interface Definition

HotChocolate tries to infer interfaces from the .Net types. When a resolver returns an interface, you just have to register the implementation on the schema builder. HotChocolate will register the types as implementations of the interface.

In the annotation based approach, you most likely do not need to worry about interfaces at all.

C#
public class Query
{
public IMessage[] GetMessages([Service]IMessageRepo repo) => repo.GetMessages();
}
[GraphQLName("Message")]
public interface IMessage
{
User SendBy { get; }
DateTime CreatedAt { get; }
}
public class TextMessage : IMessage
{
public User SendBy { get; set; }
public DateTime CreatedAt { get; set; }
public string Content { get; set; }
}
// .....

Configure Services

C#
public void ConfigureServices(IServiceCollection services)
{
services
.AddRouting()
.AddGraphQLServer()
.AddQueryType<Query>()
// HotChocolate knows that TextMessage implements IMessage and will add it to the list
// of implementations
.AddType<TextMessage>()
.AddType<VideoMessage>()
}

You can also use classes as definitions for interfaces. To mark a base class as an interface definition you can use [InterfaceType].

C#
[InterfaceType]
public abstract class Message
{
public User SendBy { get; set; }
public DateTime CreatedAt { get; set; }
}

Unions

Unions are very similar to interfaces. The difference is that members of an unions do not have fields in common. Unions are useful if you have completely disjunct structured types.

SDL
type Group {
id: ID!
members: [GroupMember]
}
type User {
userName: String!
}
union GroupMember = User | Group

Querying Unions

Union types do not have fields in common.
You have to use Inline Fragments 📄 to query for fields of a specific implementation.

GraphQL
{
accessControl {
__typename
... on Group {
id
}
... on User {
userName
}
}
}
JSON
{
"accessControl": [
{
"__typename": "Group",
"id": "R3JvdXA6MQ=="
},
{
"__typename": "User",
"userName": "SpicyChicken404"
},
{
"__typename": "User",
"userName": "CookingMaster86"
}
]
}

Union Definition

In the annotation based approach, HotChocolate tries to infer union types from the .Net types. You can manage the membership of union types with a marker interface.

C#
[UnionType("GroupMember")]
public interface IGroupMember
{
}
public class Group : IGroupMember
{
[Id]
public Guid Identifier { get; set; }
public IGroupMember[] Members { get; set; }
}
public class User : IGroupMember
{
public string UserName { get; set; }
}
public class Query
{
public IGroupMember[] GetAccessControl([Service]IAccessRepo repo) => repo.GetItems();
}

Configure Services

C#
public void ConfigureServices(IServiceCollection services)
{
services
.AddRouting()
.AddGraphQLServer()
// HotChocolate will pick up IGroupMember as a UnionType<IGroupMember>
.AddQueryType<Query>()
// HotChocolate knows that User and Group implement IGroupMember and will add it to the
// list of possible types of the UnionType
.AddType<Group>()
.AddType<User>()
}