Automatic Persisted Queries on AWS using Cloudfront, Serverless and DynamoDB
Introducing DynamoDB with Apollo Server 2.0
We also mentioned an improved version to the proposed solution was adding a CDN in front of your Apollo Server 2.
A common practice is using Persistent Queries from Apollo Client as GET requests. When using GET requests we should also add a Cloudfront API Cache in front of our API Gateway since Apollo Server 2.0 includes sending Cache-Control headers along to even further reduce your Lambda and remote endpoint calls.
Not only that, combining Redis with Lambda can work well for you but what if you like DynamoDB as much as we like? How about caching persisted queries in DynamoDB ?
We have been happily using a lot of Open Source such as GraphQL and Apollo helping us to grow fast, so it is time to start giving back — releasing our first attempt DynamoDBCache for Apollo Server 2.0 but more below.
Apollo Server 2.0
With the new release of Apollo Server cache control comes built-in scanning the cache control headers set in your resolvers or defined in your GraphQL schema.
Sounds great — but how about larger queries being send as GET requests…luckily there is a solution as well — Automatic Persisted Queries. You can read more background here:
Enhance network performance and achieve faster time-to-interactiondev-blog.apollodata.com
So how do we go about this assuming you are using GraphQL with AWS API Gateway and Lamda: ‘Serverless’ GraphQL
Serverless GraphQL in AWS
Before we dive into what we came up with to get Apollo Server running with Lambda, let have a look at the architecture for running ‘Serverless’ GraphQL and Apollo Server 2.0 on AWS.
Note that you may be better off using AWS AppSync for prototypes for example but if you like more control and less dependency below will serve you well.
Cache GraphQL queries in Cloudfront
First of all you will want to cache your public data in Cloudfront for reuse at edge locations to reduce your latency, network requests and of course database calls.
A common practice and recommended approach by Apollo as well is using a CDN in front of your GraphQL API.
Assuming you are using API Gateway there is actually a few ways to go about it such as API Gateway cache or combine it with an external CDN such as Fastly or Cloudflare. We found API Gateway cache not flexible enough and possibly also to expensive in this case.
If you are already on AWS then let’s use Cloudfront. You may be using Terraform or Cloudformation for your resources — sorry below is just a few screens on the setup in the AWS Console.
A few notes here:
- When using API Gateway with Lambda you have 2 options for your endpoints regional vs edge. If you are using a multi-region setup you better use the regional endpoint option (API Gateway without Cloudfront). If you are using edge options your API Gateway has already a Cloudfront setup which means you won’t be able to have latency-based routing for your API for example.
- You can link any external domain (CNAME) to a Cloudfront distribution.
- Use HTTPS :)
- Forward the query string
- Use Origin Cache Headers (for caching supplied by Apollo Server 2.0)
Introducing Persisted Queries with DynamoDB
Apollo Server latest release candidate came with packages for cached persisted queries and data source cache in LRU, Redis and Memcached. When using Lambda with Node there are two ways now either setting callback waiting to false which can cause unpredictable behavior or use a HTTP based data storage. This one reason why DynamoDB is so popular with Lambda.
LRU is an option for Lambda but being stateless and running for a few minutes only LRU will not be a great bet.
So here we go let’s build a new package DynamoDBCache to cache persistent queries in DynamoDB having in mind that DynamoDB can serve low digit latencies for key value queries like such cases.
Apollo has built a great interface to create additional stores like we planned to build offering an Interface to a generic KeyValueCache which DynamoDB is going to be.
We believe DynamoDB is a great option because of its low latency, multi-region capability (Global tables) and flexibility with streams etc.
We only have to implement a get and set method to read and write our persisted queries to DynamoDB.
Here it is:
We have forked the Apollo Server and just created our first PR hopefully being part of Apollo Server 2.0 release soon.
We are looking forward to help others having a similar architecture using GraphQL with AWS Lambda.
You can see a sample repository here:
We are confident using DynamoDB as persistent query cache is a great start but there is certainly more we can do. With the first release we will be limited to setting only a table name and region for our cache but going forward we may add further options:
- Add DAX support
DynamoDB has it’s own sub-microsecond cache that we could use if you are using Lambda in a VPC.
- Tracing support
Adding AWS X-ray for performance traces of the package
- Add LRU as first priority
Even faster is likely to use in-memory LRU with DynamoDB as fallback
- Full query cache support
In some cases DynamoDB might also work well as cache for full queries having its limits of 400KB per item in mind
Below architecture for future caching approach with AWS Lambda, LRU, DAX and DynamoDB.
At BrikL we are excited about new technologies in the GraphQL ecosystem such as Apollo Server 2.0 where AWS, Apollo and GraphQL are an essential part of our stack.
If you are in Thailand or around, join the GraphQL meetup in Bangkok.
BrikL is a fashion tech startup providing a one-stop solution to fashion companies. Our Fashion Design App is cloud-native built on top of serverless technologies with React, Apollo, GraphQL, NodeJS, DynamoDB and more.