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
gqltag 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 with
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 the
KeyValueCache 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.
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.