slinden
slinden

Reputation: 1025

How to setup server-side full response caching in Apollo Server

I am trying to setup a cache according to this guide for one expensive query that has a result that changes only once a day. The query takes 7-8 seconds and most it is after the DB query, because the response returned from the resolver must be heavily processed.

I am using apollo-server-express library and the change pluging is apollo-server-plugin-response-cache.

This is what I have done:

server.js

const { ApolloServer } = require('apollo-server-express')
const responseCachePlugin = require('apollo-server-plugin-response-cache')

const server = new ApolloServer({
  typeDefs,
  resolvers,
  context: async ({ req }) => {
    // ...
  },
  plugins: [responseCachePlugin()]
resolvers.js

personDetails: async (root, args, ctx, info) => {
      info.cacheControl.setCacheHint({ maxAge: 600 })
      const persons = await Person.find({}).populate('details')

      // processing the data

      return processedData
}

I expect the resolver to run once and then after that the response should be returned from the cache almost instantly. This doesn't work. I am doing something wrong or I haven't understood how this should work.

I tried to put cache hints also in the schema, but didn't get any better results.

Upvotes: 4

Views: 3600

Answers (2)

Ashish Tiwari
Ashish Tiwari

Reputation: 31

I understand that this is an old question but it might help someone solve server side caching issue in Apollo.

So as per my understanding the apollo-server-plugin-response-cache plugin only works if the response from the underlying REST API have control-cache header. In case there is no cache-control header in your REST API it can be overriden in restDataSouce GET calls as below.

return this.get(`uri_path`,"", {cacheOptions: { ttl: 1}});

After that continue using info.cacheControl at the resolvers as suggested in the example from the question snippets. maxAge there will override the time mentioned in the cacheOptions.

Upvotes: 1

Lin Du
Lin Du

Reputation: 102247

It should work. Here is a working example:

server.ts:

import { ApolloServer, gql } from 'apollo-server-express';
import express from 'express';
import responseCachePlugin from 'apollo-server-plugin-response-cache';

const typeDefs = gql`
  type Query {
    personDetails: String
  }
`;
const resolvers = {
  Query: {
    personDetails: async (root, args, ctx, info) => {
      console.log(`[${new Date().toLocaleTimeString()}] Query.personDetails`);
      info.cacheControl.setCacheHint({ maxAge: 10 });
      return 'This is person details';
    },
  },
};

const app = express();
const apolloServer = new ApolloServer({
  typeDefs,
  resolvers,
  plugins: [responseCachePlugin()],
});

apolloServer.applyMiddleware({ app });

const server = app.listen({ port: 4000 }, () => {
  console.log(`The server is running in http://localhost:4000${apolloServer.graphqlPath}`);
});

The request logs:

The server is running in http://localhost:4000/graphql
[1:51:27 PM] Query.personDetails
[1:51:52 PM] Query.personDetails

The response header:

cache-control: max-age=10, public

The first graphql request send at [1:51:27 PM]. In the next 10 seconds, all requests sent will hit the cache(defaults to an in-memory LRU cache), which means the graphql resolver personDetails will not execute. The graphql response will be read from the cache and sent to client-side.

After 10 seconds, I send another graphql request at [1:51:52 PM]. The cache is expired. So this request will not hit the in-memory cache. The graphql resolver will execute and generate a new value.

source code: https://github.com/mrdulin/apollo-graphql-tutorial/tree/master/src/stackoverflow/57243105

Upvotes: 3

Related Questions