Extending Types

We are still working on the documentation for Hot Chocolate 11.1 so help us by finding typos, missing things or write some additional docs with us.

In GraphQL we only have one query, mutation, and subscription type. These types can become huge, which makes them hard to maintain. To divide types into separate definitions, GraphQL allows to extend types.

GraphQL
type Query {
foo: String
}
extend type Query {
bar: String
}

Note: Every single code example will be shown in three different approaches, annotation-based (previously known as pure code-first), code-first, and schema-first. However, they will always result in the same outcome on a GraphQL schema perspective and internally in Hot Chocolate. All three approaches have their pros and cons and can be combined when needed with each other. If you would like to learn more about the three approaches in Hot Chocolate, click on Coding Approaches.

C#
public class Query
{
public string GetFoo() => ...
}
[ExtendObjectType(typeof(Query))]
public class QueryExtensions
{
public string GetBar() => ...
}

Note: Type extensions need to be registered with the GraphQL configuration. If you are using ASP.NET core head over to your Startup.cs and add the type extension with AddTypeExtension to your schema.

C#
services
.AddGraphQLServer()
.AddQueryType<Query>()
.AddTypeExtension<QueryExtensions>();

Extending types with the annotation-based approach

Extending types can be beneficial even with non-root types. Let's say we are building a schema with the annotation-based approach where we use pure C# to describe our types.

Given is the following entity that we do want to extend in our graph with additional fields.

C#
public class Session
{
public int Id { get; set; }
[Required]
[StringLength(200)]
public string? Title { get; set; }
[StringLength(4000)]
public string? Abstract { get; set; }
public int? TrackId { get; set; }
}

Replace a field

We could start adding our GraphQL concerns to this type directly. But often, we want to keep our entity clean from any graph concerns.

To replace the TrackId with a field Track that returns the Tack object we could do the following.

C#
[ExtendObjectType(typeof(Session))]
public class SessionResolvers
{
[BindMember(nameof(Session.TrackId))]
public async Task<Track> GetTrackAsync([Parent] Session session) => ...
}

Remove a field

We also easily can remove properties that we do not like on our initial type. For instance, let us omit the Abstract.

C#
[ExtendObjectType(
typeof(Session),
IgnoreProperties = new[] { nameof(Session.Abstract) })]
public class SessionResolvers
{
}

Add a field

Further, might we want to be able to add new fields to our entity.

C#
[ExtendObjectType(typeof(Session))]
public class SessionResolvers
{
public async Task<IEnumerable<Speaker>> GetSpeakersAsync([Parent] Session session) => ...
}

Select types to extend

Moreover, we can extend multiple types at once by extending upon base types or interfaces.

C#
[ExtendObjectType(typeof(object))] // <-- we are now extending every type that inherits from object (essentially every type).
public class SessionResolvers
{
public string SayHello() => "Hello";
}

We can also extend multiple types at once with a type but dedicate specific resolvers to specific types.

C#
[ExtendObjectType(typeof(object))] // <-- we are now extending every type that inherits from object (essentially every type)
public class SessionResolvers
{
public string Abc([Parent] Session session) => "abc"; // <-- we are only adding this field to the Session type
public string Def([Parent] Track track) => "def"; // <-- we are only adding this field to the Track type
}

Instead of using typeof(object) as a selector for extending types you can also use interfaces or other base types.

Select types to extend with schema types

We also can use schema types as a type selector.

C#
[ExtendObjectType(typeof(ObjectType))] // <-- we are now extending every object type.
public class SessionResolvers
{
public string Abc([Parent] Session session) => "abc"; // <-- we are only adding this field to the Session type
public string Def([Parent] Track track) => "def"; // <-- we are only adding this field to the Track type
}

Note, that all of the advanced type extension methods are also possible with code-first.