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.