Skip to content

Instantly share code, notes, and snippets.

@royanon
Last active December 4, 2023 04:46
Show Gist options
  • Save royanon/9038b06cf67ee245c9ab5babf9e9a34b to your computer and use it in GitHub Desktop.
Save royanon/9038b06cf67ee245c9ab5babf9e9a34b to your computer and use it in GitHub Desktop.
Sample to use apollo-server-plugin-response-cache for Strapi V4
// Work on the following dependency
// "@strapi/strapi": "4.5",
// "@strapi/plugin-graphql": "4.5",
// "apollo-server-cache-redis": "^3.3.0",
// "apollo-server-core": "^3.3.0",
// "apollo-server-plugin-response-cache": "^3.3.0",
// file location: config/plugin.js
const apolloServerPluginResponseCache = require('apollo-server-plugin-response-cache').default;
const InMemoryLRUCache = require('@apollo/utils.keyvaluecache').InMemoryLRUCache;
const ApolloServerPluginCacheControl = require('apollo-server-core').ApolloServerPluginCacheControl;
const DEFAULT_MAX_AGE = parseInt(process.env.STRAPI_GRAPHQL_DEFAULT_MAX_AGE? process.env.STRAPI_GRAPHQL_DEFAULT_MAX_AGE : 3600);
const MAX_AGE = parseInt(process.env.STRAPI_GRAPHQL_MAX_AGE? process.env.STRAPI_GRAPHQL_MAX_AGE : 60);
async function sessionId(requestContext) {
// sessionId: (requestContext) => (requestContext.request.http.headers.get('sessionId') || null),
return null;
}
/** add logic if necessary */
async function shouldReadFromCache(requestContext) {
return true;
}
/** add logic if necessary */
async function shouldWriteToCache(requestContext) {
return true;
}
/** add logic if necessary */
async function extraCacheKeyData(requestContext) {
}
/** determine cache either redis or default inMemoryLRUCache */
function initCache() {
// make sure only run when instance started, but not in build time
if (process.env.REDIS_HOST) {
const { RedisCache } = require('apollo-server-cache-redis');
return new RedisCache(process.env.REDIS_HOST);
} else {
return new InMemoryLRUCache({
// ~100MiB
maxSize: Math.pow(2, 20) * 100,
// 5 minutes (in milliseconds)
ttl: 300_000,
});
}
return false;
}
/** referenced cache instance */
const graphqlCache = initCache();
module.exports = ({ env }) => ({
graphql: {
enabled: true,
config: {
defaultLimit: 100,
apolloServer: {
// for debug purpose, we can use redis-stack to do tracking
// cache: setRedisCacheIfEnvSet(),
// keep simple to use InMemoryLRUCache if only one instance is used
// data ttl folow the defaultMaxAge if not specify
cache: graphqlCache,
tracing: true,
plugins: [
// cache behavior lower age override higher age
ApolloServerPluginCacheControl({ defaultMaxAge: DEFAULT_MAX_AGE }),
// sessionId will be used for Private scope. extra logic need to added if we need to handle login users query
apolloServerPluginResponseCache({
shouldReadFromCache,
shouldWriteToCache,
extraCacheKeyData,
sessionId,
}),
{
/**
* Plugin check request.variables.flush_cache and handle purge cache logic for redisCache or inMemoryLRUCache
* Apollo plugin life cycle ref: https://www.apollographql.com/docs/apollo-server/integrations/plugins/
* request.variables.flush_cache short_cache|purge_all
* @todo change lifecycle or otherwise, will cache itself after cache is cleared
* */
async requestDidStart() {
return {
async willSendResponse(requestContext) {
if(requestContext.request?.variables?.flush_cache === "short_cache"){
if (process.env.REDIS_HOST) {
// remove keys with expiry lower than MAX_AGE
graphqlCache.client.keys('*', (err, keys) => {
keys.forEach(function (key, pos) {
graphqlCache.client.get(key, function(err, result){
const cache_time = JSON.parse(result).cacheTime;
if(Date.now() - cache_time < MAX_AGE * 1000){
graphqlCache.client.del(key, function(err, succeeded) {
});
}
});
});
});
} else {
// remove keys with expiry lower than MAX_AGE
let keys = graphqlCache.keys();
keys.forEach(key => {
let value = graphqlCache.get(key);
value.then(function(result) {
const cache_time = JSON.parse(result).cacheTime;
if(Date.now() - cache_time < MAX_AGE * 1000){
graphqlCache.delete(key);
}
});
});
}
} else if(requestContext.request?.variables?.flush_cache === "purge_all") {
if (process.env.REDIS_HOST) {
// clear RedisCache
graphqlCache.client.flushdb( function (err, succeeded) {
// console.log(succeeded);
});
} else {
// clear inMemoryLRUCache
graphqlCache.clear();
}
}
},
};
},
},
]
},
},
},
});
@royanon
Copy link
Author

royanon commented Dec 4, 2023

The library is now available in here https://github.com/10Life/strapi-plugin-advanced-cache-manager

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment