diff --git a/CHANGELOG.md b/CHANGELOG.md index a88e999bbc9..c343ffd8fef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,12 @@ The version headers in this history reflect the versions of Apollo Server itself ## vNEXT +- _Nothing yet! Stay tuned._ + +## v3.10.0 + - Add `document`, `variables`, `headers` as an option in the `ApolloServerPluginLandingPageLocalDefault` plugins. The embedded version of Apollo Sandbox can now use these options as an initial state. [PR #6628](https://github.com/apollographql/apollo-server/pull/6628) +- Add `generateCacheKey` to `ApolloServerPluginResponseCache` to allow for custom cache keys. [PR #6655](https://github.com/apollographql/apollo-server/pull/6655) ## v3.9.0 diff --git a/docs/source/performance/caching.md b/docs/source/performance/caching.md index e362c3de3f7..b6d47385acd 100644 --- a/docs/source/performance/caching.md +++ b/docs/source/performance/caching.md @@ -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. | diff --git a/packages/apollo-reporting-protobuf/package.json b/packages/apollo-reporting-protobuf/package.json index 1f03a24a3be..e7cbbd12880 100644 --- a/packages/apollo-reporting-protobuf/package.json +++ b/packages/apollo-reporting-protobuf/package.json @@ -1,6 +1,6 @@ { "name": "apollo-reporting-protobuf", - "version": "3.3.1", + "version": "3.3.2", "description": "Protobuf format for Apollo usage reporting", "main": "generated/index.js", "types": "generated/index.d.ts", diff --git a/packages/apollo-server-azure-functions/package.json b/packages/apollo-server-azure-functions/package.json index 48e339bba27..be139fa7378 100644 --- a/packages/apollo-server-azure-functions/package.json +++ b/packages/apollo-server-azure-functions/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-azure-functions", - "version": "3.9.0", + "version": "3.10.0", "description": "Production-ready Node.js GraphQL server for Azure Functions", "keywords": [ "GraphQL", diff --git a/packages/apollo-server-cloud-functions/package.json b/packages/apollo-server-cloud-functions/package.json index 5ac2bbf741e..b9efd0014b3 100644 --- a/packages/apollo-server-cloud-functions/package.json +++ b/packages/apollo-server-cloud-functions/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-cloud-functions", - "version": "3.9.0", + "version": "3.10.0", "description": "Production-ready Node.js GraphQL server for Google Cloud Functions", "keywords": [ "GraphQL", diff --git a/packages/apollo-server-cloudflare/package.json b/packages/apollo-server-cloudflare/package.json index ae0e502a7c5..33a226c24e8 100644 --- a/packages/apollo-server-cloudflare/package.json +++ b/packages/apollo-server-cloudflare/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-cloudflare", - "version": "3.9.0", + "version": "3.10.0", "description": "Production-ready Node.js GraphQL server for Cloudflare workers", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/apollo-server-core/package.json b/packages/apollo-server-core/package.json index c9a57fd6657..3c8b8c45729 100644 --- a/packages/apollo-server-core/package.json +++ b/packages/apollo-server-core/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-core", - "version": "3.9.0", + "version": "3.10.0", "description": "Core engine for Apollo GraphQL server", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/apollo-server-express/package.json b/packages/apollo-server-express/package.json index 7c1ca83588c..0b167fe830f 100644 --- a/packages/apollo-server-express/package.json +++ b/packages/apollo-server-express/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-express", - "version": "3.9.0", + "version": "3.10.0", "description": "Production-ready Node.js GraphQL server for Express", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/apollo-server-fastify/package.json b/packages/apollo-server-fastify/package.json index 598ba795ef5..b785868213a 100644 --- a/packages/apollo-server-fastify/package.json +++ b/packages/apollo-server-fastify/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-fastify", - "version": "3.9.0", + "version": "3.10.0", "description": "Production-ready Node.js GraphQL server for Fastify", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/apollo-server-hapi/package.json b/packages/apollo-server-hapi/package.json index f5a88e12022..a48c29e886f 100644 --- a/packages/apollo-server-hapi/package.json +++ b/packages/apollo-server-hapi/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-hapi", - "version": "3.9.0", + "version": "3.10.0", "description": "Production-ready Node.js GraphQL server for Hapi", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/apollo-server-integration-testsuite/package.json b/packages/apollo-server-integration-testsuite/package.json index 41254147bfc..d3d7915edb1 100644 --- a/packages/apollo-server-integration-testsuite/package.json +++ b/packages/apollo-server-integration-testsuite/package.json @@ -1,7 +1,7 @@ { "name": "apollo-server-integration-testsuite", "private": true, - "version": "3.9.0", + "version": "3.10.0", "description": "Apollo Server Integrations testsuite", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/apollo-server-koa/package.json b/packages/apollo-server-koa/package.json index 21df56cc757..71d50e44c29 100644 --- a/packages/apollo-server-koa/package.json +++ b/packages/apollo-server-koa/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-koa", - "version": "3.9.0", + "version": "3.10.0", "description": "Production-ready Node.js GraphQL server for Koa", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/apollo-server-lambda/package.json b/packages/apollo-server-lambda/package.json index 22570aec14f..e21582a422e 100644 --- a/packages/apollo-server-lambda/package.json +++ b/packages/apollo-server-lambda/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-lambda", - "version": "3.9.0", + "version": "3.10.0", "description": "Production-ready Node.js GraphQL server for AWS Lambda", "keywords": [ "GraphQL", diff --git a/packages/apollo-server-micro/package.json b/packages/apollo-server-micro/package.json index 9929e0aba40..ec503d21c77 100644 --- a/packages/apollo-server-micro/package.json +++ b/packages/apollo-server-micro/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-micro", - "version": "3.9.0", + "version": "3.10.0", "description": "Production-ready Node.js GraphQL server for Micro", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/apollo-server-plugin-base/package.json b/packages/apollo-server-plugin-base/package.json index 1c0abb85214..f641a175215 100644 --- a/packages/apollo-server-plugin-base/package.json +++ b/packages/apollo-server-plugin-base/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-plugin-base", - "version": "3.6.1", + "version": "3.6.2", "description": "Apollo Server plugin base classes", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/apollo-server-plugin-response-cache/package.json b/packages/apollo-server-plugin-response-cache/package.json index 197ff253289..7d6e891bf47 100644 --- a/packages/apollo-server-plugin-response-cache/package.json +++ b/packages/apollo-server-plugin-response-cache/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-plugin-response-cache", - "version": "3.6.1", + "version": "3.7.0", "description": "Apollo Server full query response cache", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/apollo-server-plugin-response-cache/src/ApolloServerPluginResponseCache.ts b/packages/apollo-server-plugin-response-cache/src/ApolloServerPluginResponseCache.ts index 45009f88c65..93e36e584b4 100644 --- a/packages/apollo-server-plugin-response-cache/src/ApolloServerPluginResponseCache.ts +++ b/packages/apollo-server-plugin-response-cache/src/ApolloServerPluginResponseCache.ts @@ -86,6 +86,19 @@ interface Options> { shouldWriteToCache?( requestContext: GraphQLRequestContext, ): ValueOrPromise; + + // 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?( + requestContext: GraphQLRequestContext>, + keyData: unknown, + ): string; } enum SessionMode { @@ -98,18 +111,28 @@ function sha(s: string) { return createHash('sha256').update(s).digest('hex'); } -interface BaseCacheKey { +interface BaseCacheKeyData { source: string; operationName: string | null; variables: { [name: string]: any }; extra: any; } -interface ContextualCacheKey { +interface ContextualCacheKeyData { sessionMode: SessionMode; sessionId?: string | null; } +// We split the CacheKey type into two pieces just for convenience in the code +// below. Note that we don't actually export this type publicly (the +// generateCacheKey hook gets an `unknown` argument). +type CacheKeyData = BaseCacheKeyData & ContextualCacheKeyData; + +type GenerateCacheKeyFunction = ( + requestContext: GraphQLRequestContext>, + keyData: CacheKeyData, +) => string; + interface CacheValue { // Note: we only store data responses in the cache, not errors. // @@ -126,12 +149,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) { return requestContext.operation?.operation === 'query'; } @@ -148,8 +165,11 @@ export default function plugin( 'fqc:', ); + const generateCacheKey: GenerateCacheKeyFunction = + options.generateCacheKey ?? ((_, key) => sha(JSON.stringify(key))); + let sessionId: string | null = null; - let baseCacheKey: BaseCacheKey | null = null; + let baseCacheKey: BaseCacheKeyData | null = null; let age: number | null = null; return { @@ -163,12 +183,15 @@ export default function plugin( } async function cacheGet( - contextualCacheKeyFields: ContextualCacheKey, + contextualCacheKeyFields: ContextualCacheKeyData, ): Promise { - const key = cacheKeyString({ + const cacheKeyData = { ...baseCacheKey!, ...contextualCacheKeyFields, - }); + }; + + const key = generateCacheKey(requestContext, cacheKeyData); + const serializedValue = await cache.get(key); if (serializedValue === undefined) { return null; @@ -276,12 +299,15 @@ export default function plugin( } const cacheSetInBackground = ( - contextualCacheKeyFields: ContextualCacheKey, + contextualCacheKeyFields: ContextualCacheKeyData, ): void => { - const key = cacheKeyString({ + const cacheKeyData = { ...baseCacheKey!, ...contextualCacheKeyFields, - }); + }; + + const key = generateCacheKey(requestContext, cacheKeyData); + const value: CacheValue = { data, cachePolicy: policyIfCacheable, diff --git a/packages/apollo-server-types/package.json b/packages/apollo-server-types/package.json index 0a88f88c160..fd30bf8b1a8 100644 --- a/packages/apollo-server-types/package.json +++ b/packages/apollo-server-types/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-types", - "version": "3.6.1", + "version": "3.6.2", "description": "Apollo Server shared types", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/apollo-server/package.json b/packages/apollo-server/package.json index abb0bc7c033..a1e876a50d3 100644 --- a/packages/apollo-server/package.json +++ b/packages/apollo-server/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server", - "version": "3.9.0", + "version": "3.10.0", "description": "Production ready GraphQL Server", "author": "Apollo ", "main": "dist/index.js",