website/src/docs/hotchocolate/v16/building-a-schema/interfaces.md
A GraphQL interface defines a set of fields that multiple object types share. When a field returns an interface type, the client can query the shared fields directly and use fragments to access type-specific fields. Interfaces are output-only types and cannot be used as arguments or input fields.
GraphQL schema
interface Message {
author: User!
createdAt: DateTime!
}
type TextMessage implements Message {
author: User!
createdAt: DateTime!
content: String!
}
type Query {
messages: [Message]!
}
Client query
{
messages {
createdAt
... on TextMessage {
content
}
}
}
The shared createdAt field is queried directly on the interface. The content field, which exists only on TextMessage, is accessed through an inline fragment.
Hot Chocolate maps C# interfaces and abstract classes to GraphQL interface types.
<ExampleTabs> <Implementation>// Types/IMessage.cs
[InterfaceType("Message")]
public interface IMessage
{
User Author { get; set; }
DateTime CreatedAt { get; set; }
}
// Types/TextMessage.cs
public class TextMessage : IMessage
{
public User Author { get; set; }
public DateTime CreatedAt { get; set; }
public string Content { get; set; }
}
// Types/MessageQueries.cs
[QueryType]
public static partial class MessageQueries
{
public static IMessage[] GetMessages()
{
// ...
}
}
// Program.cs
builder
.AddGraphQL()
.AddType<TextMessage>();
You must register each implementing type explicitly so Hot Chocolate knows which object types belong to the interface.
You can also use an abstract class instead of an interface:
// Types/Message.cs
[InterfaceType]
public abstract class Message
{
public User Author { get; set; }
public DateTime CreatedAt { get; set; }
}
// Types/IMessage.cs
public interface IMessage
{
User Author { get; set; }
DateTime CreatedAt { get; set; }
}
// Types/MessageType.cs
public class MessageType : InterfaceType<IMessage>
{
protected override void Configure(
IInterfaceTypeDescriptor<IMessage> descriptor)
{
descriptor.Name("Message");
}
}
// Types/TextMessage.cs
public class TextMessage : IMessage
{
public User Author { get; set; }
public DateTime CreatedAt { get; set; }
public string Content { get; set; }
}
// Types/TextMessageType.cs
public class TextMessageType : ObjectType<TextMessage>
{
protected override void Configure(
IObjectTypeDescriptor<TextMessage> descriptor)
{
descriptor.Implements<MessageType>();
}
}
// Program.cs
builder
.AddGraphQL()
.AddQueryType<QueryType>()
.AddType<TextMessageType>();
You can exclude specific fields from the GraphQL interface.
<ExampleTabs> <Implementation>// Types/IMessage.cs
[InterfaceType("Message")]
public interface IMessage
{
[GraphQLIgnore]
User Author { get; set; }
DateTime CreatedAt { get; set; }
}
// Types/MessageType.cs
public class MessageType : InterfaceType<IMessage>
{
protected override void Configure(
IInterfaceTypeDescriptor<IMessage> descriptor)
{
descriptor.Ignore(f => f.Author);
}
}
Use [GraphQLName] or the Name method to override inferred names.
// Types/IMessage.cs
[GraphQLName("Post")]
public interface IMessage
{
User Author { get; set; }
[GraphQLName("addedAt")]
DateTime CreatedAt { get; set; }
}
You can also specify the name through the [InterfaceType] attribute:
[InterfaceType("Post")]
public interface IMessage
// Types/MessageType.cs
public class MessageType : InterfaceType<IMessage>
{
protected override void Configure(
IInterfaceTypeDescriptor<IMessage> descriptor)
{
descriptor.Name("Post");
descriptor
.Field(f => f.CreatedAt)
.Name("addedAt");
}
}
Both produce the following schema:
interface Post {
author: User!
addedAt: DateTime!
}
GraphQL interfaces can implement other interfaces, forming a hierarchy.
<ExampleTabs> <Implementation>// Types/IMessage.cs
[InterfaceType("Message")]
public interface IMessage
{
User Author { get; set; }
}
// Types/IDatedMessage.cs
[InterfaceType("DatedMessage")]
public interface IDatedMessage : IMessage
{
DateTime CreatedAt { get; set; }
}
// Types/TextMessage.cs
public class TextMessage : IDatedMessage
{
public User Author { get; set; }
public DateTime CreatedAt { get; set; }
public string Content { get; set; }
}
// Program.cs
builder
.AddGraphQL()
.AddType<IDatedMessage>()
.AddType<TextMessage>();
// Types/DatedMessageType.cs
public class DatedMessageType : InterfaceType<IDatedMessage>
{
protected override void Configure(
IInterfaceTypeDescriptor<IDatedMessage> descriptor)
{
descriptor.Name("DatedMessage");
descriptor.Implements<MessageType>();
}
}
// Types/TextMessageType.cs
public class TextMessageType : ObjectType<TextMessage>
{
protected override void Configure(
IObjectTypeDescriptor<TextMessage> descriptor)
{
descriptor.Implements<DatedMessageType>();
}
}
// Program.cs
builder
.AddGraphQL()
.AddQueryType<QueryType>()
.AddType<DatedMessageType>()
.AddType<TextMessageType>();
Register intermediate interfaces (like DatedMessage) explicitly if they are not returned directly from a resolver field.