apollo-server/docs/source/features/cdn.md
Evans Hauser 65d7b100e4
CDN cache-control headers (#1138)
* core: return response object from runHttpQuery

* core: change gqlResponse to graphqlResponse and add custom RequestInit type

* core: add cache-control headers based on the calcualted maxAge

* core: add extensions check during cache-control header creation

* core: create headers when cacheControl is not enabled otherwise pass through extensions

* express: initial tests of CDN cach-contol headers

* core: fixed tests with applyMiddleware and pass cacheControl config

* core: cache hint fixes, ignore when no maxAge, and check for rootKeys

* core: check for hints of length 0

* core: node 10 fails file upload test for some stream reason

* docs: add cdn caching section to features

* add space after // in comments

* fix feedback: proxy alignment and response creation

Adds cache-control toggles for http header calculation and stripping out
the cache control extensions from the respose.

Brings the default calculation of headers in line with the proxy.

* fix links in comments

* fix tests with null dereference

* update cdn docs and migration guide to include latest cdn configuration

* add not for engine migration to set engine to false

* add engine set to false in migration guide

* express: fixed tests

* address feedback to use omit and documentation

* docs: cdn caching is alternative to full response caching

* add back epipe check in upload tests
2018-06-21 13:29:14 -07:00

4.5 KiB

title description
CDN Integration Getting content delivery networks to cache GraphQL responses

Content-delivery networks such as fly.io, Cloudflare, Akamai or Fastly allow content caching close to clients, delivering data with low latency from a nearby server. Apollo Server makes it straightforward to use CDNs with GraphQL queries to cache full responses while still executing more dynamic queries.

To use Apollo Server behind a CDN, we define which GraphQL responses the CDN is allowed to cache. On the client, we set up automatic persisted queries to ensure that GraphQL requests are in a format that a CDN can understand.

Step 1: Add cache hints to the GraphQL schema

Add cache hints as directives to GraphQL schema so that Apollo Server knows which fields and types are cacheable and for how long. For example, this schema indicates that all fields that return an Author should be cached for 60 seconds, and that the posts field should itself be cached for 180 seconds:

type Author @cacheControl(maxAge: 60) {
  id: Int
  firstName: String
  lastName: String
  posts: [Post] @cacheControl(maxAge: 180)
}

See the cache control documentation for more details, including how to specify hints dynamically inside resolvers, how to set a default maxAge for all fields, and how to specify that a field should be cached for specific users only (in which case CDNs should ignore it). For example, to set a default max age other than 0 modify the Apollo Server constructor to include cacheControl:

const server = new ApolloServer({
  typeDefs,
  resolvers,
  // The max age is calculated in seconds
  cacheControl: { defaultMaxAge: 5 },
});

After this step, Apollo Server will serve the HTTP Cache-Control header on fully cacheable responses, so that any CDN in front of Apollo Server will know which responses can be cached and for how long! A "fully cacheable" response contains only data with non-zero maxAge; the header will refer to the minimum maxAge value across the whole response, and it will be public unless some of the data is tagged scope: PRIVATE. To observe this header, use any browser's network tab in its dev tools.

Step 2: Enable automatic persisted queries

Often, GraphQL requests are big POST requests and most CDNs will only cache GET requests. Additionally, GET requests generally work best when the URL has a bounded size. Enabling automatic persisted queries means that short hashes are sent over the wire instead of full queries, and Apollo Client can be configured to use GET requests for those hashed queries.

To do this, update the client code. First, add the package:

npm install apollo-link-persisted-queries

Then, add the persisted queries link to the Apollo Client constructor before the HTTP link:

import { createPersistedQueryLink } from "apollo-link-persisted-queries";
import { createHttpLink } from "apollo-link-http";
import { InMemoryCache } from "apollo-cache-inmemory";
import { ApolloLink } from "apollo-link";
import ApolloClient from "apollo-client";

ApolloLink.from([
  createPersistedQueryLink({ useGETForHashedQueries: true }),
  createHttpLink({ uri: "/graphql" })
]);

const client = new ApolloClient({
  cache: new InMemoryCache(),
  link: link
});

Make sure to include useGETForHashedQueries: true. Note that the client will still use POSTs for mutations, because it's generally best to avoid GETs for non-idempotent requests.

If configured correctly, browser's dev tools should verify that queries are now sent as GET requests, and receive appropriate Cache-Control response headers.

Step 3: Set up a CDN!

How exactly this works depends on exactly which CDN you chose. Configure your CDN to send requests to Apollo Server. Some CDNs may need to be specially configured to honor origin Cache-Control headers; for example, here is Akamai's documentation on that setting. If all is well, cacheable queries should now be saved by the CDN!

Note that requests served directly by a CDN will not show up in the Engine dashboard.