GraphQL on Azure: Part 2 - dotnet and App Service

aaronpowell

Aaron Powell

Posted on July 21, 2020

GraphQL on Azure: Part 2 - dotnet and App Service

In my introductory post we saw that there are many different ways in which you can host a GraphQL service on Azure and today we’ll take a deeper look at one such option, Azure App Service, by building a GraphQL server using dotnet. If you’re only interested in the Azure deployment, you can jump forward to that section. Also, you’ll find the complete sample on my GitHub.

Getting Started

For our server, we’ll use the graphql-dotnet project, which is one of the most common GraphQL server implementations for dotnet.

First up, we’ll need an ASP.NET Core web application, which we can create with the dotnet cli:

dotnet new web
Enter fullscreen mode Exit fullscreen mode

Next, open the project in an editor and add the NuGet packages we’ll need:

<PackageReference Include="GraphQL.Server.Core" Version="3.5.0-alpha0046" />
<PackageReference Include="GraphQL.Server.Transports.AspNetCore" Version="3.5.0-alpha0046" />
<PackageReference Include="GraphQL.Server.Transports.AspNetCore.SystemTextJson" Version="3.5.0-alpha0046" />
Enter fullscreen mode Exit fullscreen mode

At the time of writing graphql-dotnet v3 is in preview, we’re going to use that for our server but be aware there may be changes when it is released.

These packages will provide us a GraphQL server, along with the middleware needed to wire it up with ASP.NET Core and use System.Text.Json as the JSON seralizer/deserializer (you can use Newtonsoft.Json if you prefer with this package).

We’ll also add a package for GraphiQL, the GraphQL UI playground, but it’s not needed or recommended when deploying into production.

<PackageReference Include="GraphQL.Server.Ui.Playground" Version="3.5.0-alpha0046" />
Enter fullscreen mode Exit fullscreen mode

With the packages installed, it’s time to setup the server.

Implementing a Server

There are a few things that we need when it comes to implementing the server, we’re going to need a GraphQL schema, some types that implement that schema and to configure our route engine to support GraphQL’s endpoints. We’ll start by defining the schema that’s going to support our server and for the schema we’ll use a basic trivia app (which I’ve used for a number of GraphQL demos in the past). For the data, we’ll use Open Trivia DB.

.NET Types

First up, we’re going to need some generic .NET types that will represent the underlying data structure for our application. These would be the DTOs (Data Transfer Objects) that we might use in Entity Framework, but we’re just going to run in memory.

public class Quiz
{
    public string Id
    {
        get
        {
            return Question.ToLower().Replace(" ", "-");
        }
    }
    public string Question { get; set; }
    [JsonPropertyName("correct_answer")]
    public string CorrectAnswer { get; set; }
    [JsonPropertyName("incorrect_answers")]
    public List<string> IncorrectAnswers { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

As you can see, it’s a fairly generic C# class. We’ve added a few serialization attributes to help converting the JSON to .NET, but otherwise it’s nothing special. It’s also not usable with GraphQL yet and for that, we need to expose the type to a GraphQL schema, and to do that we’ll create a new class that inherits from ObjectGraphType<Quiz> which comes from the GraphQL.Types namespace:

public class QuizType : ObjectGraphType<Quiz>
{
    public QuizType()
    {
        Name = "Quiz";
        Description = "A representation of a single quiz.";
        Field(q => q.Id, nullable: false);
        Field(q => q.Question, nullable: false);
        Field(q => q.CorrectAnswer, nullable: false);
        Field<NonNullGraphType<ListGraphType<NonNullGraphType<StringGraphType>>>>("incorrectAnswers");
    }
}
Enter fullscreen mode Exit fullscreen mode

The Name and Description properties are used provide the documentation for the type, next we use Field to define what we want exposed in the schema and how we want that marked up for the GraphQL type system. We do this for each field of the DTO that we want to expose using a lambda like q => q.Id, or by giving an explicit field name (incorrectAnswers). Here’s also where you control the schema validation information as well, defining the nullability of the fields to match the way GraphQL expects it to be represented. This class would make a GraphQL type representation of:

type Quiz {
    id: String!
    question: String!
    correctAnswer: String!
    incorrectAnswers: [String!]!
}
Enter fullscreen mode Exit fullscreen mode

Finally, we want to expose a way to query our the types in our schema, and for that we'll need a Query that inherits ObjectGraphType:

public class TriviaQuery : ObjectGraphType
{
    public TriviaQuery()
    {
        Field<NonNullGraphType<ListGraphType<NonNullGraphType<QuizType>>>>("quizzes", resolve: context =>
        {
            throw new NotImplementedException();
        });

        Field<NonNullGraphType<QuizType>>("quiz", arguments: new QueryArguments() {
            new QueryArgument<NonNullGraphType<StringGraphType>> { Name = "id", Description = "id of the quiz" }
        },
        resolve: (context) => {
            throw new NotImplementedException();
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

Right now there is only a single type in our schema, but if you had multiple then the TriviaQuery would have more fields with resolvers to represent them. We've also not implemented the resolver, which is how GraphQL gets the data to return, we'll come back to that a bit later. This class produces the equivalent of the following GraphQL:

type TriviaQuery {
    quizzes: [Quiz!]!
    quiz(id: String!): Quiz!
}
Enter fullscreen mode Exit fullscreen mode

Creating a GraphQL Schema

With the DTO type, GraphQL type and Query type defined, we can now implement a schema to be used on the server:

public class TriviaSchema : Schema
{
    public TriviaSchema(TriviaQuery query)
    {
        Query = query;
    }
}
Enter fullscreen mode Exit fullscreen mode

Here we would also have mutations and subscriptions, but we're not using them for this demo.

Wiring up the Server

For the Server we integrate with the ASP.NET Core pipeline, meaning that we need to setup some services for the Dependency Injection framework. Open up Startup.cs and add update the ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<HttpClient>();
    services.AddSingleton<QuizData>();
    services.AddSingleton<TriviaQuery>();
    services.AddSingleton<ISchema, TriviaSchema>();

    services.AddGraphQL(options =>
    {
        options.EnableMetrics = true;
        options.ExposeExceptions = true;
    })
    .AddSystemTextJson();
}
Enter fullscreen mode Exit fullscreen mode

The most important part of the configuration is lines 8 - 13, where the GraphQL server is setup and we're defining the JSON seralizer, System.Text.Json. All the lines above are defining dependencies that will be injected to other types, but there's a new type we've not seen before, QuizData. This type is just used to provide access to the data store that we're using (we're just doing in-memory storage using data queried from Open Trivia DB), so I'll skip its implementation (you can see it on GitHub).

With the data store available, we can update TriviaQuery to consume the data store and use it in the resolvers:

public class TriviaQuery : ObjectGraphType
{
    public TriviaQuery(QuizData data)
    {
        Field<NonNullGraphType<ListGraphType<NonNullGraphType<QuizType>>>>("quizzes", resolve: context => data.Quizzes);

        Field<NonNullGraphType<QuizType>>("quiz", arguments: new QueryArguments() {
            new QueryArgument<NonNullGraphType<StringGraphType>> { Name = "id", Description = "id of the quiz" }
        },
        resolve: (context) => data.FindById(context.GetArgument<string>("id")));
    }
}
Enter fullscreen mode Exit fullscreen mode

Once the services are defined we can add the routing in:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseGraphQLPlayground();
    }

    app.UseRouting();

    app.UseGraphQL<ISchema>();
}
Enter fullscreen mode Exit fullscreen mode

I've put the inclusion GraphiQL. within the development environment check as that'd be how you'd want to do it for a real app, but in the demo on GitHub I include it every time.

Now, if we can launch our application, navigate to https://localhost:5001/ui/playground and run the queries to get some data back.

Deploying to App Service

With all the code complete, let’s look at deploying it to Azure. For this, we’ll use a standard Azure App Service running the latest .NET Core (3.1 at time of writing) on Windows. We don’t need to do anything special for the App Service, it’s already optimised to run an ASP.NET Core application, which is all this really is. If we were using a different runtime, like Node.js, we’d follow the standard setup for a Node.js App Service.

To deploy, we’ll use GitHub Actions, and you’ll find docs on how to do that already written. You’ll find the workflow file I’ve used in the GitHub repo.

With a workflow committed and pushed to GitHub and our App Service waiting, the Action will run and our application will be deployed. The demo I created is here.

Conclusion

Throughout this post we’ve taken a look at how we can create a GraphQL server running on ASP.NET Core using graphql-dotnet and deploy it to an Azure App Service.

When it comes to the Azure side of things, there’s nothing different we have to do to run the GraphQL server in an App Service than any other ASP.NET Core application, as graphql-dotnet is implemented to leverage all the features of ASP.NET Core seamlessly.

Again, you’ll find the complete sample on my GitHub for you to play around with yourself.

💖 💪 🙅 🚩
aaronpowell
Aaron Powell

Posted on July 21, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related