Caching mechanism is not storing data in Redis

Viewed 433

I am quite desperate right now and I am looking for any kind of help. I am trying to setup cache mechanism in my project using GraphQL and Redis. This is how I configure GraphQLModule:

    GraphQLModule.forRoot({
            cache: new BaseRedisCache({
                client: new Redis({
                    host: 'localhost',
                    port: 6379,
                    password: 'Zaq1xsw@',
                }),
                cacheControl: {
                    defaultMaxAge: 10000
                },
            }),
            plugins: [
                responseCachePlugin()
            ],
            autoSchemaFile: path.resolve(__dirname, `../generated/schema.graphql`),

            installSubscriptionHandlers: true,
        }),

This is how I’ve created queries and mutations:

    @Resolver()
    export class AuthResolver {
constructor(
    private readonly prismaService: PrismaService,
    private readonly authService: AuthService,
){}

@Query(returns => String)
async testowe(@Args(`input`) input: String, @Info() info: any) {
    info.cacheControl.setCacheHint({ maxAge: 5000, scope: 'PUBLIC' });
    return 'test';
}}

When I am using GraphQL Playground and try this query I get the response and header looks like that:

    HTTP/1.1 200 OK
    X-Powered-By: Express
    Access-Control-Allow-Origin: *
    Content-Type: application/json; charset=utf-8
    cache-control: max-age=5000, public
    Content-Length: 28
    ETag: W/"1c-2Df/lONPXcLzs1yVERHhOmONyns"
    Date: Tue, 28 Dec 2021 21:35:11 GMT
    Connection: keep-alive
    Keep-Alive: timeout=5

As You may see there is a part with “cache-control”. My problem is that I cannot see any keys or values stored in Redis. I am connected to Redis server with redis-cli tool and Ive tried “KEYS ‘*’” command. There is nothing stored in Redis.

Also I have problem with more complex queries - I do not even get a header with “cache-control” part.

Do You have any idea what I am doing wrong here? Should I be able to see stored values in Redis with such approach? Thank You in advance for any advice.

2 Answers

For what i can see, you don't tell your resolver to store it's result in Redis. The Apollo Server docs are not super clear about this.

I've did a research project around caching & graphql so feel free to read my Medium post about it: https://medium.com/@niels.onderbeke.no/research-project-which-is-the-best-caching-strategy-with-graphql-for-a-big-relational-database-56fedb773b97

But to answer your question, I've implemented Redis with GraphQL this way:

Create a function that handles the caching, like so:

export const globalTTL: number = 90;

export const checkCache = async (
  redisClient: Redis,
  key: string,
  callback: Function,
  maxAge: number = globalTTL
): Promise<Object | Array<any> | number> => {
  return new Promise(async (resolve, reject) => {
    redisClient.get(key, async (err, data) => {
      if (err) return reject(err);
      if (data != null) {
        return resolve(JSON.parse(data));
        // logger.info("read from cache");
      } else {
        // logger.info("read from db");
        let newData = await callback();
        if (!newData) newData = null;
        redisClient.setex(key, maxAge, JSON.stringify(newData));
        resolve(newData);
      }
    });
  });
};

Then in your resolver, you can call this function like so:

  @Query(() => [Post])
  async PostsAll(@Ctx() ctx: any, @Info() info: any) {
    const posts = await checkCache(ctx.redisClient, "allposts", async () => {
      return await this.postService.all();
    });
    return posts;
  }

You have to pass your Redis client in to the context of GraphQL, that way you can acces your client inside your resolver using ctx.redisClient ...

This is how I've passed it:

  const apolloServer = new ApolloServer({
    schema,
    context: ({ req, res }) => ({
      req,
      res,
      redisClient: new Redis({
        host: "redis",
        password: process.env.REDIS_PASSWORD,
      }),
    }),
  });

This way you should be able to store your data in your Redis cache.

The info.cacheControl.setCacheHint({ maxAge: 5000, scope: 'PUBLIC' }); way you are trying is for using another caching strategy within Apollo Server. Apollo is able to calculate the cache-control header with this information, but you have to set this setting then:

const apolloServer = new ApolloServer({
    schema,
      plugins: [
        ApolloServerPluginCacheControl({
          // Cache everything for 1 hour by default.
          defaultMaxAge: 3600,
          // Send the `cache-control` response header.
          calculateHttpHeaders: true,
        }),
      ],
  });

Note: You can set the default max-age to a value that suits your needs.

Hope this solves your problem!

You can find my implementation of it at my research repo: https://github.com/OnderbekeNiels/research-project-3mct/tree/redis-server-cache

I faced the same problem. Try the following

GraphQLModule.forRoot({
  plugins: [
    responseCachePlugin({
      cache: new BaseRedisCache({
        client: new Redis({
          host: 'localhost',
          port: 6379,
          password: 'Zaq1xsw@',
        }),
      }),
    })
  ],
  autoSchemaFile: path.resolve(__dirname, `../generated/schema.graphql`),
  installSubscriptionHandlers: true,
}),
Related