- Part 1 - Getting Started
- Part 2 - App Service with dotnet
- Part 4 - CosmosDB and GraphQL
- Part 5 - Can We Make GraphQL Type Safe in Code?
- Part 6 - GraphQL Subscriptions with SignalR
- Part 7 - Server-Side Authentication
- Part 8 - Logging
- Part 9 - REST to GraphQL
- Part 10 - Synthetic GraphQL Custom Responses
- Part 11 - Avoiding DoS in queries
For the server, we’ll use the tooling provided by Apollo, specifically their server integration with Azure Functions, which will make it place nicely together.
We’ll create a new project using Azure Functions, and scaffold it using the Azure Functions Core Tools:
--language flag to
Next, to host the GraphQL server we’ll need a Http Trigger, which will create a HTTP endpoint in which we can access our server via:
--name can be anything you want, but let’s make it clear that it’s providing GraphQL.
Now, we need to add the Apollo server integration for Azure Functions, which we can do with
Note: if you are using TypeScript, you need to enable
esModuleInterop in your
Lastly, we need to configure the way the HTTP Trigger returns to work with the Apollo integration, so let’s open
function.json within the
graphql folder, and change the way the HTTP response is received from the Function. By default it’s using a property of the context called
res, but we need to make it explicitly return be naming it
Implementing a Server
We’ve got out endpoint ready, it’s time to start implementing the server, which will start in the
graphql/index.ts file. Let’s replace it with this chunk:
Let’s talk about what we did here, first up we imported the
ApolloServer which is the server that will handle the incoming requests on the HTTP Trigger, we use that as the very bottom by creating the instance and exporting the handler as the module export.
Next, we imported
gql, which is a template literal that we use to write our GraphQL schema in. The schema we’ve created here is pretty basic, it only has a single type,
Query on it that has a single member to output.
Lastly, we’re creating an object called
resolvers, which are the functions that handle the request when it comes in. You’ll notice that this object mimics the structure of the schema we provided to
gql, by having a
Query property which then has a function matching the name of the available queryable values.
This is the minimum that needs to be done and if you fire up
func start you can now query the GraphQL endpoint, either via the playground of from another app.
Implementing our Quiz
Let’s go about creating a more complex solution, we’ll implement the same Quiz that we did in dotnet.
We’ll start by defining the schema that we’ll have on our server:
Now we have two types defined,
TriviaQuery, then we’ve added a root node to the schema using the
schema keyword and then stating that the
query is of type
With that done, we need to implement the resolvers to handle when we request data.
This will compile and run, mostly because GraphQL doesn’t type check that the resolver functions are implemented, but you’ll get a bunch of errors, so instead we’ll need implement the
quiz resolver handlers.
Handling a request
Let’s implement the
The function will receive 4 arguments, you’ll find them detailed on Apollo’s docs, but for this handler we really only need one of them,
context, and that will be how we’ll get access to our backend data source.
For the purposes of this blog, I’m skipping over the implementation of the data source, but you’ll find it on my github.
You might be wondering how the server knows about the data store and how it got on that
context argument. This is another thing we can provide to Apollo server when we start it up:
dataStore is something imported from another module.
Context gives us dependency injection like features for our handlers, so they don’t need to establish data connections themselves.
If we were to open the GraphQL playground and then execute a query like so:
We’ll get an error back that
Quiz.correctAnswer is a non-null field but we gave it null. The reason for this is that our storage type has a field called
correct_answer, whereas our model expects it to be
correctAnswer. To address this we’ll need to do some field mapping within our resolver so it knows how to resolve the field.
This is a resolver chain, it’s where we tell the resolvers how to handle sub-fields of an object and it acts just like a resolver itself, so we have access to the same context and if we needed to do another DB lookup, we could.
Note: These resolvers will only get called if the fields are requested from the client. This avoids loading data we don’t need.
You can go ahead and implement the
quiz resolver handler yourself, as it’s now time to deploy to Azure.
Disabling GraphQL Playground
We probably don’t want the Playground shipping to production, so we’d need to disable that. That’s done by setting the
playground property of the
ApolloServer options to
false. For that we can use an environment variable (and set it in the appropriate configs):
For the sample on GitHub, I’ve left the playground enabled.
Deploying to Azure Functions
With all the code complete, let’s look at deploying it to Azure. For this, we’ll use a standard Azure Function running the latest Node.js runtime for Azure Functions (Node.js 12 at the time of writing). We don’t need to do anything special for the Functions, it’s already optimised to run a Node.js Function with a HTTP Trigger, which is all this really is. If we were using a different runtime, like .NET, we’d follow the standard setup for a .NET Function app.
To deploy, we’ll use GitHub Actions, and you’ll find docs on how to do that already written, and I’ve done a video on this as well. 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.
When it comes to the Azure side of things, there’s nothing different we have to to do run the GraphQL server in Azure Functions, it’s just treated as a HTTP Trigger function and Apollo has nice bindings to allow us to integrate the two platforms together.
Again, you’ll find the complete sample on my GitHub for you to play around with yourself.