I’ve been hearing more and more about GraphQL in recent years. As a developer primarily focused on the Microsoft stack, my response typically starts and ends with “oh, that sounds cool.” I mean, what options do we have in ASP.NET Core? This article will discuss what GraphQL is, its differences from REST, and how to create a GraphQL endpoint with ASP.NET Core.
Contents
Challenges with Traditional APIs
Let’s start with a common scenario—you’re in charge of building out a brand new web application. It won’t see a ton of usage, but you still want to impress your boss and develop a solid foundation for the future. To create a fluid UI, so you decide on a popular SPA framework for the frontend. You aren’t too interested in the details, something like Angular or React will do. For data access, you settle on building out a RESTful API. You’re happy with your decision, thinking REST APIs are flexible and will suit your needs as the application grows.
You build out the application, which consists primarily of CRUD views, and your boss LOVES it. In fact, your boss wants you to add some more functionality to the application. As you get the new requirements, you notice a few additional use-cases. There are now scenarios where you only need a subset of the data from the existing REST endpoints. In other cases, you need more fields than is present in the current API. This circumstance brings you to a decision point.
- Should you modify your API to return a union of the data that is required by all consumers?
- Should you create new APIs that contain the exact information you need in each scenario?
- Should you make a set of parameters to determine which of the fields to return?
The first option will result in bloated APIs. The other two do not feel very RESTful. You continually find yourself asking, “Should I modify my existing APIs or create new ones for each use-case?” Each time, you make your decision and move on, but ultimately you begin to wonder if relying on a RESTful API was the best choice in this case.
I have seen this situation occur many times in many different application architectures. In rich SPA frameworks, most of the application logic resides on the clientside. Does REST do the best job of bridging the gap between the clientside and the serverside in these client-heavy architectures?
What is GraphQL?
Rather than creating explicit APIs with defined request and responses,
GraphQL creates a schema that defines the data with which clients can query and interact. GraphQL is an API specification that Facebook publicly released in 2015 as an alternative to traditional RESTful APIs. Rather than encapsulating query logic behind the API, GraphQL allows clients to request and filter needed data. Applications with a rich client-side framework or APIs that need to support a wide range of clients are just a few of the great use-cases for GraphQL.
How Does GraphQL Work?
As previously stated, a primary component of a GraphQL API is the schema. The GraphQL type system defines an API’s Schema as a collection of objects and types. An invoice object, for example, might look something like this.
type Invoice {
id: ID!
date: Date
total: Float!
items: [InvoiceLine]
}
type Invoice { id: ID! date: Date total: Float! items: [InvoiceLine] }
type Invoice { id: ID! date: Date total: Float! items: [InvoiceLine] }
Just like any object-oriented programming language, types consist of a set of fields. Fields themselves are assigned a type that can either be a predefined scalar type or another type in the schema. In the example above, we also see the ! and [] used to annotate the type. In GraphQL ! means the field is required, and [] means the field is an array.
In addition to custom defined types, two default types exist in GraphQL schemas.
- Query
- Mutation
These types act as an entry point into the API and define how a client can interact with the types. Below is an example of a standard Query type, which includes our previously described Invoice object.
type Query {
invoices: Invoice
}
type Query { invoices: Invoice }
type Query { invoices: Invoice }
For further reading, check out the documentation at grapql.org.
Getting Started with GraphQL in ASP.NET Core
Now that I’ve got you excited about GraphQL, I have some good news and bad news. The bad news is there is no support for GraphQL built directly into ASP.NET Core as of today. I suspect as more momentum builds behind GraphQL, this may change somewhere down the road. The good news is we do not have to wait to start using GraphQL in our ASP.NET Core applications. Thanks to the open-source community and the contributors to graphql-dotnet, we can get started today!
With a fresh new ASP.NET Core web application, we can begin installing a couple of NuGet packages into your project. The following commands will install GraphQL and all required dependencies.
dotnet add package GraphQL.Server.Transports.AspNetCore
dotnet add package GraphQL.Server.Transports.AspNetCore
dotnet add package GraphQL.Server.Transports.AspNetCore
Creating The Schema
Next, we can start creating our GraphQL schema. The best place to start is by making our graph types. Keeping with the accounting theme, an Invoice graph type that reflects our previous example would look like the following class.
public class InvoiceType : ObjectGraphType<Invoice>
{
public InvoiceType()
{
Field(t => t.Id);
Field(t => t.Date, nullable: true);
Field(t => t.Total);
Field(t => t.Items, nullable: true);
}
}
public class InvoiceType : ObjectGraphType<Invoice> { public InvoiceType() { Field(t => t.Id); Field(t => t.Date, nullable: true); Field(t => t.Total); Field(t => t.Items, nullable: true); } }
public class InvoiceType : ObjectGraphType<Invoice> { public InvoiceType() { Field(t => t.Id); Field(t => t.Date, nullable: true); Field(t => t.Total); Field(t => t.Items, nullable: true); } }
After we create all our graph types, we need to make query types that define how clients can interact with our API. Below is an example of a query type that provides access to the invoice graph type.
public class AccountingQueryType : ObjectGraphType
{
public AccountingQueryType(IInvoiceRepository invoiceRepository)
{
Field<ListGraphType<InvoiceType>>(
“invoices”,
resolve: x => invoiceRepository.Get());
}
}
public class AccountingQueryType : ObjectGraphType { public AccountingQueryType(IInvoiceRepository invoiceRepository) { Field<ListGraphType<InvoiceType>>( “invoices”, resolve: x => invoiceRepository.Get()); } }
public class AccountingQueryType : ObjectGraphType { public AccountingQueryType(IInvoiceRepository invoiceRepository) { Field<ListGraphType<InvoiceType>>( "invoices", resolve: x => invoiceRepository.Get()); } }
The last part of defining our GraphQL schema is creating an implementation of the Schema
class. This class will expose our previously described query type to our GraphQL API, as shown below.
public class AccountingSchema : Schema
{
public AccountingSchema(IDependencyResolver resolver) : base(resolver)
{
Query = resolver.Resolve<AccountingQueryType>();
}
}
public class AccountingSchema : Schema { public AccountingSchema(IDependencyResolver resolver) : base(resolver) { Query = resolver.Resolve<AccountingQueryType>(); } }
public class AccountingSchema : Schema { public AccountingSchema(IDependencyResolver resolver) : base(resolver) { Query = resolver.Resolve<AccountingQueryType>(); } }
Configuring ASP.NET Core
With our schema defined, we need to configure the ASP.NET Core services and middleware to incorporate our GraphQL components. Starting with the ConfigureServices
method, we can configure GraphQL with basic settings using the following code.
public void ConfigureServices(IServiceCollection services)
{
// removed for brevity
services
.AddScoped<AccountingSchema>()
.AddScoped<IDependencyResolver>(x =>
new FuncDependencyResolver(x.GetRequiredService))
.AddGraphQL(x =>
{
x.ExposeExceptions = Environment.IsDevelopment();
x.EnableMetrics = Environment.IsDevelopment();
})
.AddGraphTypes(ServiceLifetime.Scoped);
}
public void ConfigureServices(IServiceCollection services) { // removed for brevity services .AddScoped<AccountingSchema>() .AddScoped<IDependencyResolver>(x => new FuncDependencyResolver(x.GetRequiredService)) .AddGraphQL(x => { x.ExposeExceptions = Environment.IsDevelopment(); x.EnableMetrics = Environment.IsDevelopment(); }) .AddGraphTypes(ServiceLifetime.Scoped); }
public void ConfigureServices(IServiceCollection services) { // removed for brevity services .AddScoped<AccountingSchema>() .AddScoped<IDependencyResolver>(x => new FuncDependencyResolver(x.GetRequiredService)) .AddGraphQL(x => { x.ExposeExceptions = Environment.IsDevelopment(); x.EnableMetrics = Environment.IsDevelopment(); }) .AddGraphTypes(ServiceLifetime.Scoped); }
I typically use these as my default configuration options; however, the GraphQLOptions class exposes additional properties for more fine-grained control. Next, we have to add GraphQL to our request pipeline. As shown below, we can accomplish this by adding the UseGraphQL extension method with our GraphQL schema.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// removed for brevity
app.UseGraphQL<AccountingSchema>(“/graphql”);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // removed for brevity app.UseGraphQL<AccountingSchema>(“/graphql”); }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // removed for brevity app.UseGraphQL<AccountingSchema>("/graphql"); }
Experimenting with GraphQL
With a GraphQL API built, how can we test it to validate it behaves as expected? There are a couple of options. First, let’s take a look at the UI Playground package that is provided by qraphql-dotnet. The UI Playground can get incorporated directly into a GraphQL project by including the following nuget package.
dotnet add package GraphQL.Server.Ui.Playground –version 4.0.1
dotnet add package GraphQL.Server.Ui.Playground –version 4.0.1
dotnet add package GraphQL.Server.Ui.Playground --version 4.0.1
Once installed, we can add the UI Playground to our middleware pipeline. In most cases, we will not want to include it in production deployments, so we can optionally add the middleware, as shown below. For additional configuration options, see the available properties in the GraphQLPlaygroundOptions class.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// removed for brevity
app.UseGraphQL<AccountingSchema>(“/graphql”);
if (Environment.IsDevelopment())
{
app.UseGraphQLPlayground(new GraphQLPlaygroundOptions());
}
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // removed for brevity app.UseGraphQL<AccountingSchema>(“/graphql”); if (Environment.IsDevelopment()) { app.UseGraphQLPlayground(new GraphQLPlaygroundOptions()); } }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // removed for brevity app.UseGraphQL<AccountingSchema>("/graphql"); if (Environment.IsDevelopment()) { app.UseGraphQLPlayground(new GraphQLPlaygroundOptions()); } }
With the latest changes, when we debug our application and browse to /ui/playground, we are presented with a helpful web page that allows us to interact with our GraphQL API.
If you don’t want to include the UI Playground in your application, there is another option. The very popular API collaboration tool Postman now supports GraphQL! Not only can you interact with your GraphQL API, but Postman also supports importing your GraphQL schema to enable autocomplete and other advanced features.
Summary
GraphQL is a powerful query language for APIs. It provides a lot of flexibility and puts more power in the hands of the client. For applications that have a lot of business logic client-side, GraphQL can be a great fit. While ASP.NET Core does not have native support, you can still get started building GraphQL APIs with the graphql-dotnet library.
This blog post was written originally on espressocoder.com Jason Robert.