GraphQL using .NET Boxed: Subscriptions

This post is going to continue my exploration of GraphQL using the .NET Boxed template as a jumping off point. The code I am starting with can be found here. Check out GraphQL using .NET Boxed: Mutations from last week for an exploration of mutations.

Subscriptions are GraphQL’s way of allowing a client to request notification of changes to the data.

Starting Point

As discovered a few weeks ago, MainSchema is the central point to finding how GraphQL is set up in this template. For reference here is the full class.

public class MainSchema : Schema
{
    public MainSchema(
        QueryObject query,
        MutationObject mutation,
        SubscriptionObject subscription,
        IDependencyResolver resolver)
        : base(resolver)
    {
        this.Query = resolver.Resolve<QueryObject>();
        this.Mutation = mutation;
        this.Subscription = subscription;
    }
}

Today we are interested in the Subscription property which is being assigned a SubscriptionObject.

Subscription Object

The following is the full SubscriptionObject for reference. I will point out a few details after the code.

public class SubscriptionObject : ObjectGraphType<object>
{
    public SubscriptionObject(IHumanRepository humanRepository)
    {
        this.Name = "Subscription";
        this.Description = "The subscription type, represents all updates can be pushed to the client in real time over web sockets.";

        this.AddField(
            new EventStreamFieldType()
            {
                Name = "humanCreated",
                Description = "Subscribe to human created events.",
                Arguments = new QueryArguments(
                    new QueryArgument<ListGraphType<StringGraphType>>()
                    {
                        Name = "homePlanets",
                    }),
                Type = typeof(HumanCreatedEvent),
                Resolver = new FuncFieldResolver<Human>(context => 
                                  context.Source as Human),
                Subscriber = new EventStreamResolver<Human>(context =>
                {
                    var homePlanets = 
                           context.GetArgument<List<string>>("homePlanets");
                    return humanRepository
                        .WhenHumanCreated
                        .Where(x => homePlanets == null || 
                                    homePlanets.Contains(x.HomePlanet));
                }),
            });
    }
}

A lot of this is going to look very similar to the other types we have reviewed for queries and mutations. For example, the Type is HumanCreatedEvent which derives from HumanObject which is ObjectGraphType around the Human class.

Type = typeof(HumanCreatedEvent)

One of the hardest things exploring GraphQL is getting a good handle on the object graph. I highly recommend you spend some time in these classes getting the connection solid in your mind.

As another example that should look pretty similar to things we coved in the other post is the Resolver which is dealing with the base Human type.

Resolver = new FuncFieldResolver<Human>(context => context.Source as Human)

This next bit is new and deals with the actual notification of GraphQL when the HumanRepository creates a new human. The following code has had the home planet related stuff removed for clarity.

Subscriber = new EventStreamResolver<Human>(context =>
{
    return humanRepository
        .WhenHumanCreated;
})

What is WhenHumanCreated? Looks like it is an observable provided by theHumanRepository.

public IObservable<Human> WhenHumanCreated => 
                          this.whenHumanCreated.AsObservable();

Looking at the AddHuman function you will see that this observable is provided a new value everytime a human is created which in turn provides notification to our GraphQL setup to notify any clients that are subscribed that a new human was added.

public Task<Human> AddHuman(Human human, CancellationToken cancellationToken)
{
    human.Id = Guid.NewGuid();
    Database.Humans.Add(human);
    this.whenHumanCreated.OnNext(human);
    return Task.FromResult(human);
}

Wrapping Up

That covers my exploration of subscriptions. For me, this was the coolest part of the things I have seen in GraphQL.

I know this was a bit of a strange series as we just looked at the code generated by a template. I hope you found it useful. I know it helped me get a better grip on the idea behind GraphQL and how it can be handled in .NET Core.

The associated sample code can be found here.


Also published on Medium.

1 thought on “GraphQL using .NET Boxed: Subscriptions”

  1. Hi, I also follow the similar pattern.
    How do you adjust your code when you want to retrieve data from multiple streams..
    especially one source is from external (assume via message queue) and other is from your code (some enumerable to observable).

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.