Advanced GraphQL Patterns: Embrace the AST!
by Nicola Marcacci Rossi
Apollo Server can be amazingly simple to set up. You start with a simple GraphQL schema, implement a few resolvers, and off you go. But if your use case is more complex, you need a better understanding of what’s going on behind the scenes, and, if possible, how to modify it. This is what we are going to look at in this article.
This article assumes some working knowledge with server-side GraphQL, in particular Apollo Server. It is part of a series on enterprise grade GraphQL hosted by smartive.
- Part 1: Building Enterprise Grade APIs with GraphQL, MySQL and Node.js
- Part 2: Advanced GraphQL Patterns: The Almighty Root Resolver
- Part 3: Advanced GraphQL Patterns: Embrace the AST!
- Part 4: GraphQL and MySQL: Solving the Join Problem
- Part 5: GraphQL and Elasticsearch: A Love Letter 💌
- Part 6: The most popular GraphQL Servers and Clients for Node.js
Why Explore the Internals of Apollo Server
Perhaps you feel you need to know more about the GraphQL schema:
- What does Apollo Server do with the GraphQL schema?
- Where/how can I access it from within a resolver?
- How can I transform the GraphQL schema?
- Can I generate the schema (or parts of it) programmatically?
Perhaps what’s puzzling you are the queries received by the GraphQL server:
- How can I analyze the query received from the client?
- How can I relate the query to the schema (e.g. to find custom validation directives)?
- How can I use the query to write more complex resolvers (e.g. recursively joining data in an Almighty Root Resolver)?
To answer these and many more advanced GraphQL questions, you need to become fluent in the internals of Apollo Server. More precisely, you need to understand how Apollo Server employs certain core GraphQL libraries, and how to use them to your advantage.
Abandon Hope All Ye Who Enter Here (Without TypeScript)
Warning: We are entering a scarcely documented area of GraphQL land. You are mostly on your own. TypeScript turned out to be essential for the exploration of these internals. There’s nothing like ctrl-click
(other than console.log
) to further your insight in these matters.
Understanding GraphQL Schemas
There are a number of ways to define your GraphQL schema. The most straightforward is a schema.graphql
file.
You can then define some resolvers and feed everything to Apollo Server.
OK, but what does Apollo do with that information? Where can I access it? How can I modify it?
Executable Schemas
What Apollo does in the background is:
- Parse the schema string into an Abstract Syntax Tree (AST)
- Transform the AST into a
GraphQLSchema
- Add the resolver functions to the
GraphQLSchema
object
In other words, the above Apollo initialization is equivalent to the following:
Think about it this way: the AST is a representation of the schema document, while the GraphQLSchema object is a data structure that can resolve GraphQL queries. Note that both these types are defined in the graphql
package, the core implementation of the GraphQL language.
Getting the Schema and the AST in a Resolver
So, where do we find the GraphQLSchema
object generated during initialization from a resolver? In the mysterious info
object.
In the example below, we access the schema and look for a type definition. Note that the types in a GraphQLSchema
often don’t contain all needed information. Luckily, the needed AST node is always attached to the equivalent type.
Take a moment to understand the structure of bookAst
:
As you can see, an Abstract Syntax Tree really is just that: a big plain JavaScript object that represents (part) of the original document.
A rule of thumb: The GraphQLSchema
has a few useful utility functions (e.g. to quickly find out inheritance structures), but in general you will find all information you need in AST objects. When an AST object refers to another type you will have to go find it through the GraphQLSchema
object.
Harnessing GraphQL Schemas
Knowing these things enables you to use advanced GraphQL programming patterns:
- Want to programmatically generate a schema? Build a full AST (a
DocumentNode
object), then feed it tobuildASTSchema
like in the example above. - Want to arbitrarily transform an existing schema? If you have access to the full AST (the
DocumentNode
object), modify that. I believe the reference to the full AST gets lost though. In that case, use the snippet below to print the schema to string, parse it and modify the resulting AST. - Want to use custom schema directives in your resolvers? Inspect the AST.
- Need to know specific properties of types in your resolvers? Inspect the AST.
The following function is useful if you want to reconvert a schema to string (the original printSchema
function in the graphql
package loses custom directives).
Understanding GraphQL Queries
Until here we’ve seen: Apollo turns your schema into an object and makes it available in your resolver’s info
object.
But what about the queries sent to the server? The same: Apollo turns queries into ASTs and stores them in your resolver’s info
object.
Say we send the following query to our GraphQL server:
Here’s how we find it in our resolver:
The value of fieldName
is "books"
, i.e. it lets you access the context of the current resolver. fieldNodes
looks like this:
Again, as you see, a plain JavaScript object, this time representing your query.
Note: There are more goodies in the info
object. For more info (pun unintended) check out this article.
Harnessing Query Information
With this knowledge, combined with your knowledge of the GraphQLSchema
object, you can perform a lot of magic (some of which we will be talking about in future articles), such as:
- Implementing custom validation decorators
- Autogenerating SQL Queries
- Prefetching data needed further down the query in a higher-level resolver (e.g. using SQL JOINS)
- Implementing an Almighty Root Resolver
Build Enterprise Grade GraphQL Applications
Want to become a GraphQL pro? Follow us and read our whole series on enterprise-grade GraphQL applications.
- Part 1: Building Enterprise Grade APIs with GraphQL, MySQL and Node.js
- Part 2: Advanced GraphQL Patterns: The Almighty Root Resolver
- Part 3: Advanced GraphQL Patterns: Embrace the AST!
- Part 4: GraphQL and MySQL: Solving the Join Problem
- Part 5: GraphQL and Elasticsearch: A Love Letter 💌
- Part 6: The most popular GraphQL Servers and Clients for Node.js
Need an expert team to implement your advanced web application? Check out our portfolio.