GraphQL Persisted Queries with Apollo Server 2
I know what you’re thinking, a third article on GraphQL persisted queries? What more could you possibly write about them. Well, the tech world moves fast, and the team over at Apollo is no exception. They’re doing big things over there, and it’s our responsibility to keep up. Apollo Server 2 removes a ton of boilerplate and significantly simplifies the set up process. Today, boys and girls, we’re going to delete some code.
If you haven’t read my previous post GraphQL Dynamic Persisted Queries, I suggest you start there, as this tutorial will pick up right where we left off. For those of you who are up to date with the idea of persisted queries and want to skip the previous article, you can find the project files here: source files.
No program is perfect, even the most talented engineers will write a bug or two (or three). By far the best design pattern available is simply writing less code. That’s the opportunity we have today, to accomplish our goals by doing less. If you’re following along, your GraphQL server’s package.json
file should look something like this…
Let’s start by updating Apollo Server by running the following
npm install --save apollo-server-express@latest
This will uninstall Apollo Server 1 and install Apollo Server 2, at least until version 3 arrives. Apollo Server 2 comes with a ton of packages already built in. This removes boilerplate and reduces the number of steps we took to get set up in the beginning.
Go ahead and remove the following packages, as they are all included in Apollo Server 2.
npm uninstall cors body-parser graphql-tools graphql-playground-middleware-express graphql --save
That’s better, now our package.json
file should look a little simpler.
Although not entirely necessary, Apollo does provide their own redis client called Apollo Server Cache Redis. However, we’ll stick with ioredis in this example. We’ll need to update our schemas.js
file to include the gql
tag. According to Apollo…
Apollo Server 2.0 ships with the
gql
tag for editor syntax highlighting and auto-formatting with Prettier. In the future, we will be using it for statically analyzing GraphQL queries, so Apollo Servers requires wrapping your schema withgql
.
The gql tag parses the query string into an AST and can now be exported from the apollo-server-express package.
Seems like a small but significant change, as I for one love auto formatting. Last but not least, let’s update our server.js
file. It’s mostly deletions, but a tiny bit of refactoring is needed.
So what changed? First we eliminated the cors and body-parser packages, both of which can now be passed as booleans to the new ApolloServer
constructor from apollo-server-express. cors and body-parser both have a default value of true
, technically it’s an (if not false) equality check, either way, we have no need to pass in those options here.
Next, we removed the graphqlExpress
middleware from the old version of apollo-server-express, and playground
from graphql-playground-middleware-express, which is now the default in-browser IDE instead of GraphiQL. We also removed the makeExecutableSchema
utility function from graph-tools, as the new ApolloServer
constructor will handle that work for us under the hood.
For our next trick, you’ll notice we completely removed our persistedQueries.js
middleware import. This is no mistake, everything we wrote in the previous article to manage the sha256Hash
in redis from scratch is now handled by Apollo Server 2, fresh out of the box, Ta-da! ApolloServer
uses an LRU in-memory cache by default, so persisted query hashes can be processed and saved in-memory with little to no configuration.
In some cases this may be all we need, however, in our current setup, we’ll also need to pass in an object called persistedQueries
to the ApolloServer
constructor’s list of options. That object contains a property called cache
whose value we can set to any in-memory database we need (e.g. redis). We can even provide our own custom variation as long as we follow the documented interface specified here by the apollo-server-caching package.
The signature for the set
method is a little different in ioredis. We’ll need to make a small modification to the way we set our TTL’s if we want to adhere to theKeyValueCache
interface specified above. Let’s create a file called enhancedRedis.js
and add the following to it.
Since ioredis exports a constructor we can simply extend that class and make some slight changes. We are wrapping the set
method with our own implementation that has the ability to set the TTL’s option in a configuration object. Then all we need to do is import our class instead, and pass enhancedRedis
to the ApolloServer
config object. Finally, after calling the ApolloServer
constructor, we use the return value to call applyMiddleware
. This is a required step when using a specific framework, in our case we pass in express.
Anytime we can remove over 100 lines of code and still accomplish our goals should be considered a win. At the very least, now you see why these changes deserved their very own blog post. Let’s fire up the app and make sure all is working as it should. Run the following commands, each in their own respective directory and terminal window.
Redis client
redis-server
GraphQL server
node server.js
React client
npm run start
Success! Now we have a lightweight implementation of persisted queries using Apollo Server 2. A few things worth mentioning, you may have noticed that we never removed express, even though Apollo Server 2 now comes with it’s own HTTP server built in. At some point, we may in fact no longer need express, but there is a bit more work to be done to solidify our edge caching setup. In the next article of this series, we’ll peruse some more advanced concepts, so it’ll behoove us to remain flexible. This goes for ramda as well, we’ll find a use for it soon.
Technically everything we went over in part 1 and part 2 was agnostic to whichever GraphQL Server middleware we chose. This article favors Apollo Server 2 as an example of what’s possible with the current version. We could just as easily roll back to our previous set up and switch out Apollo Server 2 with Express GraphQL, and all would work just as it should.
As always, if you had any trouble following along you can find the completed project files here. Source files.