Extending Types
Type extensions let you add, remove, or replace fields on existing types without modifying the original class. This is particularly useful for types defined in other assemblies or NuGet packages, and for organizing a large schema into domain-focused modules.
Hot Chocolate merges type extensions with the original type definition at schema build time. The resulting schema contains a single type with all fields combined. Extensions do not produce the extend type syntax in the GraphQL schema.
Adding Fields
The most common use case is adding resolver fields to an existing type.
// Types/Book.cspublic class Book{ public int Id { get; set; } public string Title { get; set; } public int AuthorId { get; set; }}
// Types/BookExtensions.cs[ExtendObjectType<Book>]public static partial class BookExtensions{ public static IEnumerable<string> GetGenres([Parent] Book book) { // ... }}
// Program.csbuilder .AddGraphQL() .AddTypeExtension<BookExtensions>();
The genres field appears on the Book type alongside the original fields.
Extending Root Types
Type extensions are how you split root types across multiple classes. Each class adds its own set of fields to the Query, Mutation, or Subscription type.
With the source generator, use [QueryType], [MutationType], or [SubscriptionType] on multiple classes. These are type extensions under the hood. See Queries for details.
If you need to extend a root type without the source generator, use [ExtendObjectType]:
// Types/BookQueries.cs[ExtendObjectType(typeof(Query))]public class BookQueries{ public IEnumerable<Book> GetBooks() { // ... }}
// Program.csbuilder .AddGraphQL() .AddTypeExtension<BookQueries>();
Removing Fields
You can exclude fields from the original type.
// Types/BookExtensions.cs[ExtendObjectType(typeof(Book), IgnoreProperties = new[] { nameof(Book.AuthorId) })]public class BookExtensions{}
// Program.csbuilder .AddGraphQL() .AddTypeExtension<BookExtensions>();
Replacing Fields
Replace a field by binding a new resolver to an existing property. This is useful for replacing foreign key IDs with resolved entities.
// Types/BookExtensions.cs[ExtendObjectType<Book>]public static partial class BookExtensions{ [BindMember(nameof(Book.AuthorId))] public static Author GetAuthor([Parent] Book book, AuthorService authors) => authors.GetById(book.AuthorId);}
This replaces the authorId: Int! field with an author: Author! field on the Book type.
Extending by Name
When you cannot reference the target type directly, extend it by its GraphQL type name.
// Types/FooExtensions.cs[ExtendObjectType("Foo")]public class FooExtensions{ public string GetExtraField() { // ... }}
For root types, use the constants in OperationTypeNames instead of string literals (for example, OperationTypeNames.Query).
Extending by Base Type
You can extend every type that inherits from a base class or implements an interface.
// Types/AuditExtensions.cs[ExtendObjectType(typeof(object))]public class AuditExtensions{ // Added to every object type public DateTime GetTimestamp() => DateTime.UtcNow;
// Added only to Book (inferred from the [Parent] type) public Author GetAuthor([Parent] Book book, AuthorService authors) => authors.GetById(book.AuthorId);}
You can also target a specific interface:
// Types/PostExtensions.cs[ExtendObjectType(typeof(IPost))]public class PostExtensions{ public string GetSummary([Parent] IPost post) { // Applied to every type that implements IPost // ... }}
The extension applies to every object type that implements IPost, not to the interface type itself.
Next Steps
- Need to define root types? See Queries and Mutations.
- Need to define object types? See Object Types.
- Need global object identification? See Relay.