@graphql-yoga/plugin-response-cache
Version:
For the documentation check `http://graphql-yoga.com/docs/response-cache`
120 lines (119 loc) • 5.59 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.createInMemoryCache = exports.useResponseCache = void 0;
const response_cache_1 = require("@envelop/response-cache");
const operationIdByRequest = new WeakMap();
const sessionByRequest = new WeakMap();
function sessionFactoryForEnvelop({ request }) {
return sessionByRequest.get(request);
}
const cacheKeyFactoryForEnvelop = async function cacheKeyFactoryForEnvelop({ context }) {
const request = context.request;
if (request == null) {
throw new Error('[useResponseCache] This plugin is not configured correctly. Make sure you use this plugin with GraphQL Yoga');
}
const operationId = operationIdByRequest.get(request);
if (operationId == null) {
throw new Error('[useResponseCache] This plugin is not configured correctly. Make sure you use this plugin with GraphQL Yoga');
}
return operationId;
};
const getDocumentStringForEnvelop = executionArgs => {
const context = executionArgs.contextValue;
if (context.params?.query == null) {
throw new Error('[useResponseCache] This plugin is not configured correctly. Make sure you use this plugin with GraphQL Yoga');
}
return context.params.query;
};
function useResponseCache(options) {
const buildResponseCacheKey = options?.buildResponseCacheKey || response_cache_1.defaultBuildResponseCacheKey;
const cache = options.cache ?? (0, exports.createInMemoryCache)();
const enabled = options.enabled ?? (() => true);
let logger;
return {
onYogaInit({ yoga }) {
logger = yoga.logger;
},
onPluginInit({ addPlugin }) {
addPlugin((0, response_cache_1.useResponseCache)({
...options,
enabled({ request }) {
return enabled(request);
},
cache,
getDocumentString: getDocumentStringForEnvelop,
session: sessionFactoryForEnvelop,
buildResponseCacheKey: cacheKeyFactoryForEnvelop,
shouldCacheResult({ cacheKey, result }) {
const shouldCached = options.shouldCacheResult
? options.shouldCacheResult({ cacheKey, result })
: !result.errors?.length;
if (shouldCached) {
const extensions = (result.extensions ||= {});
const httpExtensions = (extensions.http ||= {});
const headers = (httpExtensions.headers ||= {});
headers['ETag'] = cacheKey;
headers['Last-Modified'] = new Date().toUTCString();
}
else {
logger.warn('[useResponseCache] Failed to cache due to errors');
}
return shouldCached;
},
}));
},
async onRequest({ request, fetchAPI, endResponse }) {
if (enabled(request)) {
const operationId = request.headers.get('If-None-Match');
if (operationId) {
const cachedResponse = await cache.get(operationId);
if (cachedResponse) {
const lastModifiedFromClient = request.headers.get('If-Modified-Since');
const lastModifiedFromCache = cachedResponse.extensions?.http?.headers?.['Last-Modified'];
if (
// This should be in the extensions already but we check it here to make sure
lastModifiedFromCache != null &&
// If the client doesn't send If-Modified-Since header, we assume the cache is valid
(lastModifiedFromClient == null ||
new Date(lastModifiedFromClient).getTime() >=
new Date(lastModifiedFromCache).getTime())) {
const okResponse = new fetchAPI.Response(null, {
status: 304,
headers: {
ETag: operationId,
},
});
endResponse(okResponse);
}
}
}
}
},
async onParams({ params, request, setResult }) {
const sessionId = await options.session(request);
const operationId = await buildResponseCacheKey({
documentString: params.query || '',
variableValues: params.variables,
operationName: params.operationName,
sessionId,
request,
});
operationIdByRequest.set(request, operationId);
sessionByRequest.set(request, sessionId);
if (enabled(request)) {
const cachedResponse = await cache.get(operationId);
if (cachedResponse) {
if (options.includeExtensionMetadata) {
setResult((0, response_cache_1.resultWithMetadata)(cachedResponse, { hit: true }));
}
else {
setResult(cachedResponse);
}
return;
}
}
},
};
}
exports.useResponseCache = useResponseCache;
exports.createInMemoryCache = response_cache_1.createInMemoryCache;
;