Setting up GraphQL with a .NET Core site

27/01/2021

With growing interest in websites being built using a headless CMS, the UI team at NetConstruct have become very familiar with GraphQL APIs and the benefits it brings over traditional REST APIs.  Many headless CMS providers offer GraphQL APIs or static site generator plugins which open up the use of GraphQL for data retrieval.

One of the main attractions of using GraphQL is the ability to specify fields that need to be returned when making API calls.  This in turn reduces the size of the response payload, removing all of the bloat that would typically be associated with REST API response models.

UI developers can either use introspection queries to find the types and fields they need from the API, or they can use an IDE such as GraphiQL (we set this up later! 👇) to visually browse the schema itself.  Backend developers can define the entire schema, and it is up to the UI developer make the right decisions on what to return from the API.

No more having to ask and wait for a backend developer to modify the REST API to add or remove fields, the UI developer can just modify the query appropriately 😍 can be quite the time saver!

Setting up GraphQL

When it comes to implementing GraphQL, you have a wide range of tools to choose from, that spread across many programming languages.  You are not locked into a specific tool or programming language, as long as the GraphQL specification is followed you should be fine.

The perfect choice for me would be graphql-dotnet - I work with .NET on a daily basis and I want to set up GraphQL on Kentico Xperience's demo website built in .NET Core 3.1.

The Dancing Goat demo website has a number of classes that we could expose through a GraphQL API (Brewers, Coffees, Cafes, etc.), but to keep this example simple, the initial API will only allow developers to query cafe locations.

Once the demo website was up and running, I began by installing graphql-dotnet.  You only really need to install the middleware NuGet package, GraphQL.Server.Transports.AspNetCore.SystemTextJson.  The NuGet package already has the appropriate dependencies included to get a GraphQL endpoint up and running.  You just need to register and configure it accordingly.

In the Startup class, we first need to register the GraphQL service, and add the GraphQL middleware to the HTTP request pipeline:

// Add using directives
using GraphQL.Server;
using GraphQL.Types;

namespace DancingGoat
{
    public class Startup
    {

        public void ConfigureServices(IServiceCollection services)
        {
            ...

            // Register the GraphQL service, and the System.Text.Json deserializer
            services.AddGraphQL().AddSystemTextJson();
        }


        public void Configure(IApplicationBuilder app, IWebHostEnvironment environment)
        {
            ...

            // Add the GraphQL middleware to the HTTP request pipeline
            app.UseGraphQL<ISchema>();
        }
    }
}

Now the GraphQL middleware has been set up, we should provide it with a schema as the middleware is now expecting to have an ISchema implementation available.  So lets create one!

This DancingGoatSchema class inherits from Schema, which implements the ISchema interface.  All we want to do here, is register a RootQuery which will be the entry point for our GraphQL API.

using DancingGoat.GraphQL.Queries;
using GraphQL.Types;
using GraphQL.Utilities;
using System;

namespace DancingGoat.GraphQL.Schemas
{
    public class DancingGoatSchema : Schema
    {
        public DancingGoatSchema(IServiceProvider provider) : base(provider)
        {
            Query = provider.GetRequiredService<RootQuery>();
        }
    }
}

You're probably aware by now I have named the next class we need to create as RootQuery.  I have deliberately called it this because GraphQL only allows a single root query object to be registered.   You could register multiple GraphQL endpoints, and have a different RootQuery for each, but ideally we want just a single GraphQL endpoint.

To get around this, you can use Query Organization and register different sub queries below the root query.  This is how I have approached the RootQuery class below:

using GraphQL.Types;

namespace DancingGoat.GraphQL.Queries
{
    public class RootQuery : ObjectGraphType
    {
        public RootQuery()
        {
            // https://graphql-dotnet.github.io/docs/getting-started/query-organization/
            Field<CafeQuery>("cafes", resolve: context => new { });

            // in the future, we could add more sub query roots, for Brewers, Coffees, etc.
            // Field<BrewerQuery>("brewers", resolve: context => new { });
            // etc...
        }
    }
}

Our RootQuery contains a field called CafeQuery, this is our first sub query being registered, and we now need to provide a class to represent it. 

using DancingGoat.GraphQL.GraphTypes;
using DancingGoat.Models;
using GraphQL;
using GraphQL.Types;
using System.Linq;

namespace DancingGoat.GraphQL.Queries
{
    public class CafeQuery : ObjectGraphType
    {
        public CafeQuery(CafeRepository cafeRepository)
        {
            Field<ListGraphType<CafeType>>("items", resolve: context => cafeRepository.GetCompanyCafes("/"));
            Field<CafeType>("item",
                arguments: new QueryArguments(
                    new QueryArgument<StringGraphType>
                    {
                        Name = "name"
                    }
                ),
                resolve: context => cafeRepository.GetCompanyCafes("/")
                    .FirstOrDefault(x => x.Name.Equals(context.GetArgument<string>("name")))
                );
        }
    }
}

The first piece of functionality I am exposing to the GraphQL endpoint is a field called "items", this provides the ability to query and return all of the cafe documents.  We are making use of the repository that comes with Dancing Goat out of the box to do any data retrieval from the database.

The second field called "item" returns a single cafe document, which is searched based on a string argument passed in with the GraphQL query.  We are once again using the cafe repository, but this time we're filtering the results by finding the first exact match on the cafe name property.

But...we can't just return the Cafe generated model to the GraphQL API, we need to map the Cafe model to a class that inherits from ObjectGraphType.  An example of how to do this is below:

using CMS.DocumentEngine.Types.DancingGoatCore;
using GraphQL.Types;

namespace DancingGoat.GraphQL.GraphTypes
{
    public class CafeType : ObjectGraphType<Cafe>
    {
        public CafeType()
        {
            Field(x => x.Name);
            Field(x => x.Phone);
            Field(x => x.Email);
            Field(x => x.ZIP);
            Field(x => x.Street);
            Field(x => x.City);
            Field(x => x.Country);
        }
    }
}

Hang on! 🛑 The site will most likely compile now, but we haven't set up any of our new types to be available through .NET Core's dependency injection.  We need to head back to the Startup class, and add them in: 

// Add using directives
using GraphQL.Server;
using GraphQL.Types;

namespace DancingGoat
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            ...

            services.AddSingleton<ISchema, DancingGoatSchema>();
            services.AddSingleton<RootQuery>();
            services.AddSingleton<CafeQuery>();
            services.AddSingleton<CafeType>();

            // Register the GraphQL service, and the System.Text.Json deserializer
            services.AddGraphQL().AddSystemTextJson();
        }
    }
}

You should now be able to query the GraphQL endpoint by POSTing a query to http://localhost:57562/graphql.  To call the "items" part of our CafeQuery, we can use the following query: 

query CafeList {
  cafes {
    items {
      name
    }
  }
}

And you should get a response with all of the cafes and their names: 

{
    "data": {
        "cafes": {
            "items": [
                {
                    "name": "Boston"
                },
                {
                    "name": "Chicago"
                },
                {
                    "name": "Los Angeles"
                },
                {
                    "name": "New York"
                }
            ]
        }
    }
}

GraphiQL

Creating GraphQL queries on your own can be confusing at first if you are not aware of the schema definition, or you are not familiar with how to use introspection queries.

What we can do instead is set up an in-browser IDE such as GraphiQL, this will show you the entire schema in the Explorer tab, and guide you into building up your own queries.  It is also great for debugging purposes.

It's really easy to set up too, first install the NuGet package GraphQL.Server.Ui.GraphiQL.

Once again head over to the Startup class, and add the GraphiQL middleware to the HTTP request pipeline.

// Add using directives
using GraphQL.Server;
using GraphQL.Types;

namespace DancingGoat
{
    public class Startup
    {
        public void Configure(IApplicationBuilder app, IWebHostEnvironment environment)
        {
            ...

            // Add the GraphQL middleware to the HTTP request pipeline
            app.UseGraphQL<ISchema>();

            // Add the GraphiQL middleware to the HTTP request pipeline
            app.UseGraphiQLServer();
        }
    }
}

After compiling and running the site again, you should be able to access the GraphiQL IDE through http://localhost:57562/ui/graphiql/  

Example GraphiQL interface.
Example GraphiQL interface.