As part of an upcoming project, I'll be working with GraphQL. So before starting, I decided to set up a GraphQL API on danschnau.com first.
To accomplish this, I installed the projectgraphql-dotnet and configured it against the danschnau.com blog database.
Thanks to the GraphQL.MicrosoftDI package, as part of the graphql-dotnet project, setting up an API was a matter of one change in the application's ServiceCollection and one in the Application builder.
public void ConfigureServices(IServiceCollection services)
{
// other services configured....
services.AddGraphQL(b =>
{
b.Services.Register<GraphQLHttpMiddleware<ISchema>, GraphQLHttpMiddleware<ISchema>>(GraphQL.DI.ServiceLifetime.Singleton);
b.AddSystemTextJson()
.AddErrorInfoProvider(opt => opt.ExposeExceptionDetails = true) // set to false for production
.AddSchema<BlogSchema>()
.AddGraphTypes(typeof(BlogSchema).Assembly);
});
// other services configured....
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// other stuff....
app.UseGraphQL<ISchema>();
app.UseGraphQLPlayground();
// other stuff....
}
Configuring the GraphQL Schema is a matter of mapping the concepts in GraphQL to the application. GraphQL queries are always rooted in a "Query" or a "Mutation" - I think of them as GET and POST in http. In my case, I did not need a Mutation
in my Schema, but only a simple Query
for the purpose.
using GraphQL.Instrumentation;
using GraphQL.Types;
public class BlogSchema : Schema
{
public BlogSchema(IServiceProvider serviceProvider) : base(serviceProvider)
{
// Query = ....
Query = (BlogQuery)serviceProvider.GetService(typeof(BlogQuery)) ??
throw new InvalidOperationException();
// no mutations.
// Mutation = ....
}
}
The BlogQuery
defines a graphQL query that can be made, and instructions for the rest of the application to execute, or resolve
, the query.
using GraphQL.Types;
public class BlogQuery : ObjectGraphType<object>
{
public BlogQuery()
{
FieldAsync<ListGraphType<BlogType>>("blogs", "get all blogs", resolve:
async context => await blogRepo.GetBlogsAsync());
}
}
}
Lastly, the GraphQL package must learn about the types needed. In my case, a BlogType
:
using GraphQL.Types;
public class BlogType : ObjectGraphType<Blog>
{
public BlogType()
{
Name = "Blog";
Field(b => b.BlogId).Description("The id of the blog.");
Field(b => b.Published).Description("When the blog was first published.");
Field(b => b.LastUpdated, nullable: true).Description("When the blog was last updated. Null if never.");
Field(b => b.Title).Description("The Title of the Blog.");
Field(b => b.Slug).Description("The slug for the blog, used in URL generation.");
Field(b => b.Content).Description("The Content of the blog, serialized into HTML.");
Field(name: "URL", expression: b => $"https://danschnau.com/blog/{b.Slug}").Description("The URL for the blog.");
}
}
}
The result is that if you visit https://danschnau.com/ui/playground, you get a sample client which can be used to make GraphQL queries for this blog.