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.

GraphQL using .NET Boxed: Mutations

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: Queries from last week for an exploration of queries.

Mutations are GraphQL’s way of allowing a client to request changes to the data on the server-side.

Starting Point

As we discovered last week, MainSchema is the central point to finding how GraphQL is set up in this template. Just 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;
    }
}

We are interested in the Mutation property which is being assigned a MutationObject.

Mutation Object

The following is the full MutationObject class which defines what mutations are allowed for which objects on this server and what happens when a mutation request is received.

public class MutationObject : ObjectGraphType<object>
{
    public MutationObject(IHumanRepository humanRepository)
    {
        this.Name = "Mutation";
        this.Description = "The mutation type for updates to our data.";
        this.FieldAsync<HumanObject, Human>(
            "createHuman",
            "Create a new human.",
            arguments: new QueryArguments(
                new QueryArgument<NonNullGraphType<HumanInputObject>>()
                {
                    Name = "human",
                    Description = "The human you want to create.",
                }),
            resolve: context =>
            {
                var human = context.GetArgument<Human>("human");
                return humanRepository.AddHuman(human,
                                                context.CancellationToken);
            });
    }
}

This is very similar to have the QueryObject from last week was set up. The first big difference is in the QueryArguments. The mutation is taking a HumanInputObject class instead of an ID. If you look at the query argument you will also see that this argument isn’t allowed to be null.

new QueryArgument<NonNullGraphType<HumanInputObject>>()

What is the HumanInputObject class? It is an InputObjectGraphType and defines the shape of what the mutation query argument looks like. As you can see in the following it provides a name, description, and list of fields.

public class HumanInputObject : InputObjectGraphType
{
    public HumanInputObject()
    {
        this.Name = "HumanInput";
        this.Description = "A humanoid creature from Star Wars.";
        this.Field<NonNullGraphType<StringGraphType>>(nameof(Human.Name));
        this.Field<StringGraphType>(nameof(Human.HomePlanet));
        this.Field<ListGraphType<EpisodeEnumeration>>(nameof(Human.AppearsIn), "Which movie they appear in.");
    }
}

Also, note that the fields are using nameof on the properties of the Human class to make sure the names match which will prevent any problems with the mapping between the 3 different human classes this project is working with. Here is an example of the field definition pulled out from the above sample.

this.Field<NonNullGraphType<StringGraphType>>(nameof(Human.Name));

Another thing to make note of is that even at the field level you can set if a field is allowed to be null or not on top of setting the type of the field.

Back over in the MutationObject let’s look at the resolve inside of the FieldAsync call.

resolve: context =>
{
    var human = context.GetArgument<Human>("human");
    return humanRepository.AddHuman(human, context.CancellationToken);
});

This is pulling the human query argument and it is being translated into an instance of the Human class and then sent to the repository to be saved.

Wrapping Up

That covers the basic exploration of mutations. I’m thinking about looking at subscriptions.

The associated sample code can be found here.

GraphQL using .NET Boxed: Queries

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. If you want to start at the very basics of getting the .NET Boxed templates installed, check out my ASP.NET Core with GraphQL using .NET Boxed post.

Finding a Thread

As stated in the last post the backing data for the project is in a static class found in the Repositories/Database.cs file. The data is Star Wars themed and consists of two lists of characters one for droid and one for humans which get combined into a list of characters.

Instead of starting with the data and finding where it is used I’m going to approach this from the perspective of the way the application handles requests. For an ASP.NET Core application, this means looking at the Configure function of the Startup class, which is where the HTTP pipeline is configured.

In the Configure function, we find the following two calls related to GraphQL. I dropped the actual setting of options out of both calls.

.UseGraphQLWebSocket<MainSchema>(new GraphQLWebSocketsOptions())
.UseGraphQLHttp<MainSchema>(new GraphQLHttpOptions())

Looks like MainSchema is the thread we needed to follow.

MainSchema

There isn’t a lot of code in the MainSchema class. The following is the full class as it was generated by the template.

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;
    }
}

The Schema class that is the base class is provided by the GraphQL for .NET library. Just like any other schema in GraphQL it is used to define the data available to the client and the types of queries that can be used. For now, we are going to stick with following the Query type and leave Mutation and Subscription for future posts.

Query Object

For the template, we are using the schema for queries is located in the QueryObject class. Below is the class but simplified to only include the setup of one entity. For our sample Star Wars data, I am using the human bits and dropping the droids.

public class QueryObject : ObjectGraphType<object>
{
    public QueryObject(IHumanRepository humanRepository)
    {
        this.Name = "Query";
        this.Description = "The query type, represents all of the entry points into our object graph.";

        this.FieldAsync<HumanObject, Human>(
            "human",
            "Get a human by it's unique identifier.",
            arguments: new QueryArguments(
                new QueryArgument<IdGraphType>()
                {
                    Name = "id",
                    Description = "The unique identifier of the human.",
                }),
            resolve: context => humanRepository.GetHuman(
                context.GetArgument("id", 
                                    defaultValue: new Guid("94fbd693-2027-4804-bf40-ed427fe76fda")),
                context.CancellationToken));
    }
}

The class Constructor needs an instance of a class that is used to access the data for the entity the query is being defined for, in this case, an IHumanRepository.

The key bit in this class is the FieldAsync<HumanObject, Human> call. It is the definition of the query allowed for getting a single human. The first parameter is the name of the query, the second is the query’s description.

Next, are the arguments the query needs to execute.

arguments: new QueryArguments(
                new QueryArgument<IdGraphType>()
                {
                    Name = "id",
                    Description = "The unique identifier of the human.",
                })

In this example, the query can take a single argument for the ID of the human to be returned. The final bit is how this query should be resolved to the actual backing data.

resolve: context => humanRepository.GetHuman(
            context.GetArgument("id", 
                                defaultValue: new Guid("94fbd693-2027-4804-bf40-ed427fe76fda")),
            context.CancellationToken))

Here the context allows us to pull the parameters passed by the client using the context.GetArgument function call with name, as defined in the argument section, and use them in the call to the humanRepository.

Object Graph Type

The last point I want to touch on is the difference between the two types on the FieldAsync<HumanObject, Human> call. Human is the type the entity used by the data access. HumanObject is Human wrapped in an ObjectGraphType. The following is the full HumanObject class.

public HumanObject(IHumanRepository humanRepository)
{
    this.Name = "Human";
    this.Description = "A humanoid creature from the Star Wars universe.";
    this.Field(x => x.Id, 
                    type: typeof(IdGraphType))
              .Description("The unique identifier of the human.");
    this.Field(x => x.Name)
              .Description("The name of the human.");
    this.Field(x => x.HomePlanet, nullable: true)
              .Description("The home planet of the human.");
    this.Field<ListGraphType<EpisodeEnumeration>>
              (nameof(Character.AppearsIn), 
               "Which movie they appear in.");
    
    this.FieldAsync<ListGraphType<CharacterInterface>, 
                    List<Character>>(
        nameof(Human.Friends),
        "The friends of the character, or an empty list if they have none.",
        resolve: context => 
                 humanRepository.GetFriends(context.Source,
                                            context.CancellationToken));
    this.Interface<CharacterInterface>();
}

This pattern allows extra metadata to be added to the type being wrapped. You can see this work in the schema area playground that the sample application launches when in development mode.

Wrapping Up

My previous post on GraphQL left me feeling like it was pretty magical. That is true from the client side perspective. From the server side, that magic comes with some complexity. Don’t take that as a judgment on GraphQL it is just one of the aspects that must be considered before selecting a technology. As I get more familiar with the server side of GraphQL I’m sure some of what I am seeing as complexity when just learning will become clear.

The associated sample code can be found here.

ASP.NET Core with GraphQL using .NET Boxed

GraphQL is something I have been meaning to look into for a while, but haven’t found the time. Muhammad Rehan Saeed recent release of .NET Boxed, which is a rebranding of his ASP.NET Core Boilerplate templates, contains a template for GraphQL. This brought GraphQL back to the front of my mind so this post is going to walk through installing and using .NET Boxed to get a GraphQL project up and running.

Template Installation

As with all templates, installation is very easy using the .NET CLI. Open a command prompt and enter the following command.

dotnet new --install "Boxed.Templates::*"

The * at the end of the command is just making sure the latest version gets installed. After the installation is done you will get a list of all the template you have installed. The two new ones provided by .NET Boxed are highlighted in the screenshot.

Project Creation

For this post, we will be creating a project using the graphql template. Both of the templates provided by .NET Boxed have a lot of optional flags that can be used configure the resulting project. Make sure to run the help command for the template you are using to see if there are any option you want to set. For example, the following command would list all the options for the GraphQL template.

dotnet new graphql --help

Create and navigate to the directory you want the new project to be created in and then run the following command to create the project with the default setting with the exception of turning off HTTPS. Not something you want to turn off for a production application, but it reduces the complexity of the initial exploration.

dotnet new graphql --https-everywhere false

With the project created run the following command from the same directory to open it in Visual Studio Code.

code .

Some Project Notes

The project generation creates a ReadMe.html in the root of the project. Make sure and check it out. It has a list of pre-requisites needed for the project as well as a lot of good information on general good practices.

We aren’t going to dig too far into the project itself in this post, but it is helpful to know where the data lives and what it looks like for when we are trying out some GraphQL queries. The backing data is in a static class found in the Repositories/Database.cs file.

The data is Star Wars themed and consists of two lists of characters one for droid and one for humans which get combined into a list of characters. The following is the Database class for reference.

public static class Database
{
    static Database()
    {
        Droids = new List<Droid>()
        {
            new Droid()
            {
                Id = new Guid("1ae34c3b-c1a0-4b7b-9375-c5a221d49e68"),
                Name = "R2-D2",
                Friends = new List<Guid>()
                {
                    new Guid("94fbd693-2027-4804-bf40-ed427fe76fda"),
                    new Guid("c2bbf949-764b-4d4f-bce6-0404211810fa")
                },
                AppearsIn = new List<Episode>() { Episode.NEWHOPE,
                                                  Episode.EMPIRE,
                                                  Episode.JEDI, },
                PrimaryFunction = "Astromech"
            },
            new Droid()
            {
                Id = new Guid("c2bbf949-764b-4d4f-bce6-0404211810fa"),
                Name = "C-3PO",
                Friends = new List<Guid>(),
                AppearsIn = new List<Episode>() { Episode.NEWHOPE,
                                                  Episode.EMPIRE,
                                                  Episode.JEDI, },
                PrimaryFunction = "Protocol"
            }
        };
        Humans = new List<Human>()
        {
            new Human()
            {
                Id = new Guid("94fbd693-2027-4804-bf40-ed427fe76fda"),
                Name = "Luke Skywalker",
                Friends = new List<Guid>()
                {
                    new Guid("1ae34c3b-c1a0-4b7b-9375-c5a221d49e68"),
                    new Guid("c2bbf949-764b-4d4f-bce6-0404211810fa")
                },
                AppearsIn = new List<Episode>() { Episode.NEWHOPE,
                                                  Episode.EMPIRE,
                                                  Episode.JEDI, },
                HomePlanet = "Tatooine"
            },
            new Human()
            {
                Id = new Guid("7f7bf389-2cfb-45f4-b91e-9d95441c1ecc"),
                Name = "Darth Vader",
                Friends = new List<Guid>(),
                AppearsIn = new List<Episode>() { Episode.NEWHOPE,
                                                  Episode.EMPIRE,
                                                  Episode.JEDI, },
                HomePlanet = "Tatooine"
            }
        };
        Characters = Droids.AsEnumerable<Character>()
                           .Concat(Humans)
                           .ToList();
    }

    public static List<Character> Characters { get; }

    public static List<Droid> Droids { get; }

    public static List<Human> Humans { get; }
}

Try it out

When you run the project in development mode (the default) it will show a GraphQL playground which is made available by GraphQL.NET. It looks like the following.

Box 1 is the area where you enter the query you want to execute against the GraphQL backend. Box 2 is the play button which sends the query to the backend. Finally, box 3 is where the results of the query are displayed.

The following query is asking for ID, Name, and Appears In properties for the human that matches the provided ID.

query getHuman{
  human(id: "94fbd693-2027-4804-bf40-ed427fe76fda")
  {
    id,
    name,
    appearsIn    
  }
}

On our sample data, the query returns the following.

{
  "data": {
    "human": {
      "id": "94fbd693-2027-4804-bf40-ed427fe76fda",
      "name": "Luke Skywalker",
      "appearsIn": [
        "NEWHOPE",
        "EMPIRE",
        "JEDI"
      ]
    }
  }
}

Now let’s try creating a new human. Clear the query area and enter the following query.

mutation createHuman($human: HumanInput!) {
  createHuman(human: $human) {
    id
    name
  }
}

If you tried to run the above it would fail. For this query to work we need to define what $human is. You do this in the Query Variables area which is right below box 1 in the screenshot above. Enter the following and hit the play button.

{
  "human": {
    "name": "Eric Anderson",
    "homePlanet": "Earth"
  }
}

This will result in the following. Your ID will be different of course.

{
  "data": {
    "createHuman": {
      "id": "22297c0e-01b4-4322-8e25-16d455a0c8e2",
      "name": "Eric Anderson"
    }
  }
}

Wrapping Up

The above information is enough to get started playing with GraphQL with ASP.NET Core. There is a lot more that I want to dig into so look for more posts on these topics. The code can be found here.