Using an external graphql server on your server side rendered node application can slow down the initial render time.
This library provides an apollo-link and express (proxy) middleware setup to support a local @cache
directive for queries.
It comes with a default in-memory cache implementation (using Map
). You can provide your own (e.g. redis
etc.) by just
implementing a simple interface.
yarn add apollo-proxy-cache
or
npm install apollo-proxy-cache
query someQuery($arg1: String!) @cache(id: "cache-key", timeout: 3600, modifier: ["arg1"]) {
field(arg: $arg1) {
property
}
}
name | description | type | required | |
---|---|---|---|---|
id | static cache key | string |
true | |
timeout | when the cache should be invalidated (in seconds), if unset or 0 it will be cached indefinitely. | number |
false | |
modifier | path's inside any query argument that should be appended to the cache key. lodash is used for property access, so you can use . and [] delimiters (e.g. some.path[0].in.array ). A resulting cache could look like $id.$firstPathsValue.$secondPathsValue etc. |
Array<String> |
false |
The library requires to have body-parser
(see https://www.npmjs.com/package/body-parser or similar) in the middleware chain.
Please add accordingly to the setup example.
InMemoryCache
, will store all data inside aMap
RedisCache
, will store data inside redis (requires a redisclient
instance).
The Express middleware will either redirect the request to your graphql server or serve the request locally (depending on the @cache
settings for the query).
It will additionally remove the @cache
directive and forward only the pure query to your server.
There are no changes on your implementation required.
import { createProxyCacheMiddleware, InMemoryCache } from 'apollo-proxy-cache'
import express from 'express'
const queryCache = new InMemoryCache()
import bodyParser from 'body-parser'
const proxyMiddlewareFactory = proxyCacheMiddleware(queryCache)
const app = express()
app.use(bodyParser.json()) // setup body-parser before (or anything else that populates request.body).
const { directiveMiddleware, proxyMiddleware } = proxyMiddlewareFactory(
{ target: "http://graphql-server.com", changeOrigin: true } /* configuration object for http-proxy-middleware */
)
// directive middleware to remove directives and handle caching
app.use( '/graphql', directiveMiddleware)
// the proxy itself
app.use( '/graphql', proxyMiddleware)
To speed up the initial rendering you can also setup the proxyCacheLink
.
This will skip any http request and serve directly from your cache implementation.
import { createHttpLink } from 'apollo-link-http'
import { InMemoryCache as ApolloInMemoryCache } from 'apollo-cache-inmemory'
import { from } from 'apollo-link'
import { ApolloClient } from 'apollo-client'
import { proxyCacheLink, InMemoryCache } from 'apollo-proxy-cache'
const queryCache = new InMemoryCache() /* Make sure you use the same instance that you use in the middleware setup.*/
const proxyCache = proxyCacheLink(queryCache)
const cache = new ApolloInMemoryCache()
const httpLink = createHttpLink({ uri: 'http://graphql-server.com' })
return new ApolloClient({
ssrMode: true,
link: from([
proxyCache,
httpLink
]),
cache
})
Your cache implementation needs to implement the following interface (here in flow):
interface Cache<K, V> {
delete(key: K): Promise<boolean>;
get(key: K): Promise<?V>;
set(key: K, value: V, timeout: number): Promise<Cache<K, V>>;
}
You can pass a function as second argument (type CacheKeyModifier = (?string, ?Object) => ?string
) on proxyCacheLink
and proxyCacheMiddleware
that allows you to modify the key before saving. This is useful if your queries depend on a global context. e.g. a http header that modifies the result independend of the query parameters (e.g. Accept-Language
)