Adding a GraphQL Endpoint to Your ASP.NET Core API

What is GraphQL?

GraphQL is an open source specification created by Facebook that can be used to access data in APIs. GraphQL consist of two parts.

In this pic there is a consumer of the API that can be any kind of application. The consumer sends a query to the API. Part one what GraphQL specifies is the format and syntax of the query. Part two is a runtime on the API that is able to process the query. When the runtime has processed the query it typically returns JSON, but that is not set in stone by the specification.

Schema

In a REST API you typically work with models that represent the data. With GraphQL there are typically no models. There is something called a schema. This schema declares what data a consumer of the API can access. The schema also knows how to get that data using entity framework in this case.
Also instead of multiple controllers there is now just one controller that reacts to GraphQL requests. And because there has to be only one controller with a standard implementation to process GraphQL queries we can easily replace the controller with middleware. So we're ending up with an ASP.NET Core API without any controllers.

The API reacts to GET or POST requests and whether GET or POST is used there is always a query contained in the request. Not the HTTP method and URI determine what should happen in the API but the query.
In fact the GraphQL specification doesn't tie you to HTTP. HTTP is used most of the time in practice, especially with ASP.NET Core of course, but GraphQL doesn't need HTTP to function. HTTP is just a transport used to get the query to the API.
However when you use HTTP because now each piece of data is not at a unique URL anymore a downside of GraphQL could be that it's now difficult to do HTTP caching.

Installing GraphQL .NET

Since GraphQL support is not provided within ASP.NET Core, you need a Nuget package. The one most commonly used is simply called GraphQL .NET. Which is available as open source thanks to Joe McBride and all contributors. When you install the package GraphQL.Server.Transports.AspNetCore you'll get it as a dependency as well as everything you need to hook in into ASP.NET Core. Now that we're here I also install GraphQL.Server.Ui.Playground which will enable me to test the API in the way I did when I showed you the products query.

Writing a schema

Next create a simple schema. Add a schema class called in this case CarvedRockSchema derived from the schema base class.

public class CarvedRockSchema: Schema
{
    public CarvedRockSchema(IDependencyResolver resolver): base(resolver)
    {           
        Query = ..
    }
}


In the contructor we have to provide a value for at least one of the properties Query, Mutation and Subscription. For now I'll just use Query which will mean that the API will only support data retrieval. Notice that each of the properties expects an object that implements IObjectGraphType. In fact everything that the schema exposes has to implement this interface. So I can't return entities or models from it directly.
Let's abandon this class for a moment and write a graphtype representing a product first.

public class ProductType: ObjectGraphType<Product>
{
    public ProductType()
    {
        Field(t => t.Id);
        Field(t => t.Name).Description("The name of the product");
        Field(t => t.Description);
    }
}

This class derives from the ObjectGraphType base class that implements IObjectGraphType. The schema exposes metadata and when creating an ObjectGraphType you can optionally specify for which class you want to provide metadata. In this case the Product entity.
Now in the constructor I can declare fields for this graph type. Here I'm creating a field base on the Id of the product entity. Id is an int, a type a GraphQL schema can't expose directly. GraphQL.NET has a set of common built-in graph types and it will infer that it's type should be IntGraphType. In the same way I can add name and description. There's also the possibility to provide extra metadata for a field. I could for example add a description to the name field.

public class CarvedRockQuery: ObjectGraphType
{
    public CarvedRockQuery(ProductRepository productRepository)
    {
        Field<ListGraphType<ProductType>>(
            "products", 
            resolve: context => productRepository.GetAll()
        );
    }
}


Now that we have a product graph type we need another class that knows how to get products. I call it CarvedRockQuery which also derives from ObjectGraphType but this time it's not the generic version because it isn't based on an existing type.
In the constructor I declare one field, this time I explicitly say that this field must return a list of producttype objects. As you can see even list is a special graph type.
Then I give the field a name and in a lambda I can now specify where the data should come from, in other words how to data should be resolved. In the lambda I get a context object which I ignore for now. I want to return products from my repository so I use dependency injection to get it after which I call GetAll on it.
This is interesting because GetAll returns a task which I don't have to await. GraphQL .NET takes care of that for me.
Also GetAll returns instances of the Product class not instances of productType. Again GraphQL .NET takes care of the conversion.
The final step needed to finish our schema is to go back to the schema class and configure CarvedRockQuery as it's query.

    public class CarvedRockSchema: Schema
    {
        public CarvedRockSchema(IDependencyResolver resolver): base(resolver)
        {           
            Query = resolver.Resolve<CarvedRockQuery>();
        }
    }

The best way to do that is to use dependency injection again. The GraphQL schema however uses an abstraction on top of whichever dependency injector you use, in our case the one provided by ASP.NET Core. The abstraction is called IDependencyResolver which I pass on to the base constructor so it's available for the rest of the schema.
After that I can call resolve on it to get to the CarvedRockQuery instance.

Configuring ASP.NET Core


To setup GraphQL in your project and start using the scheme we created go to the startup class of your application.

    public void ConfigureServices(IServiceCollection services)
    {
        ...

        services.AddScoped<IDependencyResolver>(s => new 									FuncDependencyResolver(s.GetRequiredService));
        services.AddScoped<CarvedRockSchema>();

        services.AddGraphQL(o => { o.ExposeExceptions = false; })
            .AddGraphTypes(ServiceLifetime.Scoped);
    }

In the ConfigureServices method add the dependency resolver we used the get the query instance. I'm telling the dependency injection container that when something asks for IDependencyResolver it has to return and instance of FuncDependencyResolver. In the lambda I get the IServiceCollection instance so I can simply call GetRequiredService on it to hook it up with the internal dependency injector in ASP.NET Core.

Then, don't forget to add the schema itself to the container. Next I'm using the AddGraphQL extension method to register all the types GraphQL .NET uses. You can use the provided options object to do some configuration. One is to expose detailed exceptions in the JSON response yes or no. I enable that by specifying true, but it might be an option to let it depend on the fact if the application is running in developer mode or not.
Now that this configuration is done, I'm also calling AddGraphTypes which will scan the assembly for all ObjectGraphTypes, such as our ProductType and register them automatically in the container using the specified lifetime.


Now in the configure method we have to activate the GraphQL middleware calling UseGraphQL of the schema you're using.

    public void Configure(IApplicationBuilder app)
    {
        app.UseGraphQL<CarvedRockSchema>();
        app.UseGraphQLPlayground(new GraphQLPlaygroundOptions());
    }

As an argument you can specify the relative URI of the endpoint, if you don't it will just be /graphql.
We don't need MVC here at all. It is not needed because the endpoint is setup using the GraphQL middleware. Of course you can still activate MVC if needed to let the API support both REST-style and GraphQL-style calls.

To play around with the API without it, give this API a playground UI by plugging an extra piece of middleware in. It's coming from the extra NuGet package we installed. I'm just giving it a new instance of GraphQLPlaygroundOptions that by default sets up the playground middleware at /ui/playground and expects the graphql endpoint to be at /graphql, which is also the default for the graphql middleware.

Playing around

If you want to see the playground UI as soon as you start the API go to the properties of the project and select the debug tab. Activate launch browser there and type in ui/playground as the url.


Now run the app.

When the browser opens take a look at the schema tab on the right side. The metadata of the schema has been read by the playground.
Using this metadata information the query editor can have intellisense.
Type the query in the pic. It gets all products but only the names and descriptions. When I execute it you can see that the result is JSON and the data is contained in the data root node which has a products array with the data I asked for.

To see how this playground app knows about the schema and underlying types, start a query with an underscore. The schema explorer doesn't show these metadata types but they are there.
This is just the beginning of your query skills of course!

The complete code is here.

To see a more detailed explaination of this see my Pluralsight course.

Tweet Post Share Update Email RSS