skip to Main Content

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


  1. 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

    Login or Signup to reply.
  2. 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,
    }),
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search