Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Adding generateCacheKey to ApolloServerPluginResponseCache to allow for custom cache keys #6655

Merged
merged 11 commits into from Jul 11, 2022
1 change: 1 addition & 0 deletions docs/source/performance/caching.md
Expand Up @@ -472,3 +472,4 @@ In addition to [the `sessionId` function](#identifying-users-for-private-respons
| `extraCacheKeyData` | This function's return value (any JSON-stringifiable object) is added to the key for the cached response. For example, if your API includes translatable text, this function can return a string derived from `requestContext.request.http.headers.get('Accept-Language')`. |
| `shouldReadFromCache` | If this function returns `false`, Apollo Server _skips_ the cache for the incoming operation, even if a valid response is available. |
| `shouldWriteToCache` | If this function returns `false`, Apollo Server doesn't cache its response for the incoming operation, even if the response's `maxAge` is greater than `0`. |
| `generateCacheKey` | Customize generation of the cache key. By default, this is the SHA256 hash of the JSON encoding of an object containing relevant data. |
Expand Up @@ -86,8 +86,23 @@ interface Options<TContext = Record<string, any>> {
shouldWriteToCache?(
requestContext: GraphQLRequestContext<TContext>,
): ValueOrPromise<boolean>;

// This hook allows one to replace the function that is used to create a cache
// key. By default, it is the SHA-256 (from the Node `crypto` package) of the result of
// calling `JSON.stringify(keyData)`. You can override this to customize the serialization
// or the hash, or to make other changes like adding a prefix to keys to allow for
// app-specific prefix-based cache invalidation. You may assume that `keyData` is an object
// and that all relevant data will be found by the kind of iteration performed by
// `JSON.stringify`, but you should not assume anything about the particular fields on
// `keyData`.
generateCacheKey?: GenerateCacheKeyFunction;
}

type GenerateCacheKeyFunction = (
requestContext: GraphQLRequestContext<Record<string, any>>,
keyData: unknown,
) => string;

enum SessionMode {
NoSession,
Private,
Expand Down Expand Up @@ -126,12 +141,6 @@ interface CacheValue {
cacheTime: number; // epoch millis, used to calculate Age header
}

type CacheKey = BaseCacheKey & ContextualCacheKey;

function cacheKeyString(key: CacheKey) {
return sha(JSON.stringify(key));
}

function isGraphQLQuery(requestContext: GraphQLRequestContext<any>) {
return requestContext.operation?.operation === 'query';
}
Expand All @@ -148,6 +157,11 @@ export default function plugin(
'fqc:',
);

const generateCacheKey: GenerateCacheKeyFunction =
options.generateCacheKey
? options.generateCacheKey
: (_, key) => sha(JSON.stringify(key));
kschrade marked this conversation as resolved.
Show resolved Hide resolved

let sessionId: string | null = null;
let baseCacheKey: BaseCacheKey | null = null;
let age: number | null = null;
Expand All @@ -165,10 +179,13 @@ export default function plugin(
async function cacheGet(
contextualCacheKeyFields: ContextualCacheKey,
): Promise<GraphQLResponse | null> {
const key = cacheKeyString({
const cacheKeyData = {
...baseCacheKey!,
...contextualCacheKeyFields,
});
};

const key = generateCacheKey(requestContext, cacheKeyData);

const serializedValue = await cache.get(key);
if (serializedValue === undefined) {
return null;
Expand Down Expand Up @@ -278,10 +295,13 @@ export default function plugin(
const cacheSetInBackground = (
contextualCacheKeyFields: ContextualCacheKey,
): void => {
const key = cacheKeyString({
const cacheKeyData = {
...baseCacheKey!,
...contextualCacheKeyFields,
});
};

const key = generateCacheKey(requestContext, cacheKeyData);

const value: CacheValue = {
data,
cachePolicy: policyIfCacheable,
Expand Down