GraphQL with ASP.NET Core (Part- II : Middleware)

Read the previous part - GraphQL with ASP.NET Core Part- I : Hello World


If you are familiar with ASP.NET Core middleware[1], you may have noticed that in our previous post we already had a middleware. In the initial blank app, that middleware was responsible for throwing a Hello World response. Later we replaced it with our custom code so that it can respond back a result of some static GraphQL query.

Middleware is software that's assembled into an application pipeline to handle requests and responses. Each component:

  • Chooses whether to pass the request to the next component in the pipeline.
  • Can perform work before and after the next component in the pipeline is invoked.

- Microsoft Documentation

Practically a middleware is a delegate or more precisely; a request delegate. As the name suggests, it handles incoming request and decides whether or not to delegate it to the next middleware in the pipeline. In our case, we configured a request delegate using the Run() (extension) method of IApplicationBuider. Between three extension methods (Use, Run, Map), Run() terminates the request for further modifications in the request pipeline.

The code inside our middleware was very simple and can only response back result of a hardcoded static query. However, in a real world scenario the query should be dynamic hence we must read it from the incoming request.

Every request delegate accepts a HttpContext. If the query is posted over a http request, you can easily read the request body using the following code,

string body;  
using (var streamReader = new StreamReader(httpContext.Request.Body))  
{
    body = await streamReader.ReadToEndAsync();
}

Validating the request first before reading its content won't do any harm. So, let's put an if clause that checks two things,

  • Is it a POST request?
  • Has it been made to a specific URL i.e. api/graphql?

So, the code will be modified as follows,

if(context.Request.Path.StartsWithSegments("/api/graphql") && string.Equals(context.Request.Method, "POST", StringComparison.OrdinalIgnoreCase))  
{
    string body;
    using (var streamReader = new StreamReader(context.Request.Body))
    {
        body = await streamReader.ReadToEndAsync();
    }

....
....
....

A request body can contain a whole lot of fields, but let's say the passed in graphql query comes within a field named query. So we can parse out the JSON string content of the body into a complex type that contains a Query property,

The complex type looks as follows,

public class GraphQLRequest
{
    public string Query { get; set; }
}

Next thing to do is deserialize the body to an instance of GraphQLRequest type using Json.Net's JsonConvert.DeserializeObject and replace the previous hardcoded query with the request.Query,

var request = JsonConvert.DeserializeObject<GraphQLRequest>(body);


var result = await new DocumentExecuter().ExecuteAsync(doc =>
{
    doc.Schema = schema;
    doc.Query = request.Query;
}).ConfigureAwait(false);

So, after all the modifications, code in the Run method of Startup.cs looks as follows,

app.Run(async (context) =>
{
    if (context.Request.Path.StartsWithSegments("/api/graphql")
   && string.Equals(context.Request.Method, "POST", StringComparison.OrdinalIgnoreCase))
    {
        string body;
        using (var streamReader = new StreamReader(context.Request.Body))
        {
            body = await streamReader.ReadToEndAsync();

            var request = JsonConvert.DeserializeObject<GraphQLRequest>(body);
            var schema = new Schema { Query = new HelloWorldQuery() };

            var result = await new DocumentExecuter().ExecuteAsync(doc =>
            {
                doc.Schema = schema;
                doc.Query = request.Query;
            }).ConfigureAwait(false);

            var json = new DocumentWriter(indent: true).Write(result);
            await context.Response.WriteAsync(json);
        }
    }
});

Now you can make a POST request containing the query field using any rest client (Postman/Insomnia),

We are pretty much done with this post. But you can see that we have a lot of newing ups of objects like the new DocumentExecuter(), new Schema(), new DocumentWriter() etc. In the next post, we will see how we can use the built-in dependency system of ASP.NET Core and make them injectable.

Repository Link (Branch)

Part II

ASP.NET Core Middleware


Read the next part - GraphQL with ASP.NET Core Part- III : Dependency Injection