diff --git a/CHANGELOG.md b/CHANGELOG.md
index e1dd0811d9a..d5d8aa2233f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@ The version headers in this history reflect the versions of Apollo Server itself
- Remove internal dependency on `apollo-server-caching`, switch over to `@apollo/utils.keyvaluecache`. This PR specifically also introduces Keyv as an unbounded cache solution, but will replace with our own simple implementation in a follow-up PR targeting this minor version release. [PR #6522](https://github.com/apollographql/apollo-server/pull/6522)
- Remove dependency on `keyv`/`@apollo/utils.keyvadapter` in favor of a simple `Map`-backed cache which implements TTL [PR #6535](https://github.com/apollographql/apollo-server/pull/6535)
+- Add `cache: "bounded"` configuration option, allowing users to opt into bounded request cache (recommended) [PR #6536](https://github.com/apollographql/apollo-server/pull/6536)
## v3.8.2
diff --git a/docs/source/api/apollo-server.mdx b/docs/source/api/apollo-server.mdx
index 544f313176f..3cc6fd38110 100644
--- a/docs/source/api/apollo-server.mdx
+++ b/docs/source/api/apollo-server.mdx
@@ -20,6 +20,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true, // highly recommended
+ cache: 'bounded',
});
```
@@ -228,6 +229,25 @@ Available in Apollo Server v3.4.0 and later.
+
@@ -726,6 +746,7 @@ async function startApolloServer() {
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});
diff --git a/docs/source/api/plugin/cache-control.md b/docs/source/api/plugin/cache-control.md
index 31e85398e68..e21cbedef6c 100644
--- a/docs/source/api/plugin/cache-control.md
+++ b/docs/source/api/plugin/cache-control.md
@@ -21,6 +21,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: "bounded",
plugins: [
ApolloServerPluginCacheControl({
// Cache everything for 1 second by default.
@@ -42,6 +43,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: "bounded",
plugins: [ApolloServerPluginCacheControlDisabled()],
});
```
diff --git a/docs/source/api/plugin/drain-http-server.mdx b/docs/source/api/plugin/drain-http-server.mdx
index d7c7f3d1e5b..261dc0ad2e8 100644
--- a/docs/source/api/plugin/drain-http-server.mdx
+++ b/docs/source/api/plugin/drain-http-server.mdx
@@ -39,6 +39,7 @@ async function startApolloServer() {
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});
diff --git a/docs/source/api/plugin/inline-trace.md b/docs/source/api/plugin/inline-trace.md
index 3ed2615b17d..43e8a12c083 100644
--- a/docs/source/api/plugin/inline-trace.md
+++ b/docs/source/api/plugin/inline-trace.md
@@ -21,6 +21,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: "bounded",
plugins: [
ApolloServerPluginInlineTrace({
rewriteError: (err) => err.message.match(SENSITIVE_REGEX) ? null : err,
@@ -39,6 +40,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: "bounded",
plugins: [ApolloServerPluginInlineTraceDisabled()],
});
```
diff --git a/docs/source/api/plugin/landing-pages.md b/docs/source/api/plugin/landing-pages.md
index 25d1e23a839..c4595cb8674 100644
--- a/docs/source/api/plugin/landing-pages.md
+++ b/docs/source/api/plugin/landing-pages.md
@@ -29,17 +29,19 @@ To configure these default plugins while still using same `NODE_ENV`-based logic
```js
import { ApolloServer } from "apollo-server";
-import { ApolloServerPluginLandingPageLocalDefault,
- ApolloServerPluginLandingPageProductionDefault
+import {
+ ApolloServerPluginLandingPageLocalDefault,
+ ApolloServerPluginLandingPageProductionDefault
} from "apollo-server-core";
const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: "bounded",
plugins: [
// Install a landing page plugin based on NODE_ENV
- process.env.NODE_ENV === 'production'
+ process.env.NODE_ENV === "production"
? ApolloServerPluginLandingPageProductionDefault({
graphRef: "my-graph-id@my-graph-variant",
footer: false,
@@ -428,6 +430,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: "bounded",
plugins: [
ApolloServerPluginLandingPageGraphQLPlayground(),
],
@@ -512,6 +515,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: "bounded",
plugins: [
ApolloServerPluginLandingPageDisabled(),
],
diff --git a/docs/source/api/plugin/schema-reporting.md b/docs/source/api/plugin/schema-reporting.md
index 10d3a5d521f..80547bcc647 100644
--- a/docs/source/api/plugin/schema-reporting.md
+++ b/docs/source/api/plugin/schema-reporting.md
@@ -23,6 +23,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: "bounded",
plugins: [
ApolloServerPluginSchemaReporting(),
],
diff --git a/docs/source/api/plugin/usage-reporting.md b/docs/source/api/plugin/usage-reporting.md
index 7ae01520b7b..edc82631a2d 100644
--- a/docs/source/api/plugin/usage-reporting.md
+++ b/docs/source/api/plugin/usage-reporting.md
@@ -25,6 +25,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: "bounded",
plugins: [
ApolloServerPluginUsageReporting({
fieldLevelInstrumentation: 0.5,
@@ -454,6 +455,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: "bounded",
plugins: [ApolloServerPluginUsageReportingDisabled()],
});
```
diff --git a/docs/source/builtin-plugins.md b/docs/source/builtin-plugins.md
index 1cb2c7ef176..de66fa819bd 100644
--- a/docs/source/builtin-plugins.md
+++ b/docs/source/builtin-plugins.md
@@ -31,6 +31,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: "bounded",
plugins: [
// Sets a non-default option on the usage reporting plugin
ApolloServerPluginUsageReporting({
diff --git a/docs/source/data/data-sources.mdx b/docs/source/data/data-sources.mdx
index 672bcdec87a..10104a5857c 100644
--- a/docs/source/data/data-sources.mdx
+++ b/docs/source/data/data-sources.mdx
@@ -55,6 +55,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: "bounded",
dataSources: () => {
return {
moviesAPI: new MoviesAPI(),
diff --git a/docs/source/data/errors.mdx b/docs/source/data/errors.mdx
index 44ddbc28c3d..babf00169c1 100644
--- a/docs/source/data/errors.mdx
+++ b/docs/source/data/errors.mdx
@@ -343,6 +343,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: "bounded",
formatError: (err) => {
// Don't give the specific errors to the client.
if (err.message.startsWith('Database Error: ')) {
@@ -398,6 +399,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: "bounded",
plugins: [
ApolloServerPluginUsageReporting({
rewriteError(err) {
@@ -429,6 +431,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: "bounded",
plugins: [
ApolloServerPluginUsageReporting({
rewriteError(err) {
@@ -467,6 +470,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: "bounded",
plugins: [
ApolloServerPluginUsageReporting({
rewriteError(err) {
@@ -512,6 +516,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
plugins: [setHttpPlugin],
});
```
diff --git a/docs/source/data/file-uploads.mdx b/docs/source/data/file-uploads.mdx
index 79c3e60f5b3..4fdf60debe4 100644
--- a/docs/source/data/file-uploads.mdx
+++ b/docs/source/data/file-uploads.mdx
@@ -79,6 +79,7 @@ async function startServer() {
resolvers,
// Using graphql-upload without CSRF prevention is very insecure.
csrfPrevention: true,
+ cache: 'bounded',
});
await server.start();
@@ -181,6 +182,7 @@ const start = async () => {
resolvers,
// Using graphql-upload without CSRF prevention is very insecure.
csrfPrevention: true,
+ cache: 'bounded',
});
// Start Apollo Server
diff --git a/docs/source/data/resolvers.mdx b/docs/source/data/resolvers.mdx
index 3ae46b62ab1..3e5344db43b 100644
--- a/docs/source/data/resolvers.mdx
+++ b/docs/source/data/resolvers.mdx
@@ -145,7 +145,12 @@ const resolvers = {
// Pass schema definition and resolvers to the
// ApolloServer constructor
-const server = new ApolloServer({ typeDefs, resolvers, csrfPrevention: true });
+const server = new ApolloServer({
+ typeDefs,
+ resolvers,
+ csrfPrevention: true,
+ cache: 'bounded',
+});
// Launch the server
server.listen().then(({ url }) => {
@@ -323,7 +328,12 @@ const resolvers = {
// Pass schema definition and resolvers to the
// ApolloServer constructor
-const server = new ApolloServer({ typeDefs, resolvers, csrfPrevention: true });
+const server = new ApolloServer({
+ typeDefs,
+ resolvers,
+ csrfPrevention: true,
+ cache: 'bounded',
+});
// Launch the server
server.listen().then(({ url }) => {
@@ -388,6 +398,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
context: ({ req }) => ({
authScope: getScope(req.headers.authorization)
})
diff --git a/docs/source/data/subscriptions.mdx b/docs/source/data/subscriptions.mdx
index cf22ea95702..7628b542430 100644
--- a/docs/source/data/subscriptions.mdx
+++ b/docs/source/data/subscriptions.mdx
@@ -82,6 +82,7 @@ To run both an Express app _and_ a separate WebSocket server for subscriptions,
const server = new ApolloServer({
schema,
csrfPrevention: true,
+ cache: "bounded",
});
```
@@ -108,6 +109,7 @@ To run both an Express app _and_ a separate WebSocket server for subscriptions,
const server = new ApolloServer({
schema,
csrfPrevention: true,
+ cache: "bounded",
plugins: [
// Proper shutdown for the HTTP server.
ApolloServerPluginDrainHttpServer({ httpServer }),
@@ -168,6 +170,7 @@ const serverCleanup = useServer({ schema }, wsServer);
const server = new ApolloServer({
schema,
csrfPrevention: true,
+ cache: "bounded",
plugins: [
// Proper shutdown for the HTTP server.
ApolloServerPluginDrainHttpServer({ httpServer }),
diff --git a/docs/source/deployment/azure-functions.mdx b/docs/source/deployment/azure-functions.mdx
index c54e4f47fc6..66aa5de6d6a 100644
--- a/docs/source/deployment/azure-functions.mdx
+++ b/docs/source/deployment/azure-functions.mdx
@@ -95,7 +95,12 @@ const resolvers = {
};
// Create our server.
-const server = new ApolloServer({ typeDefs, resolvers, csrfPrevention: true });
+const server = new ApolloServer({
+ typeDefs,
+ resolvers,
+ csrfPrevention: true,
+ cache: 'bounded',
+});
exports.graphqlHandler = server.createHandler();
```
diff --git a/docs/source/deployment/gcp-functions.mdx b/docs/source/deployment/gcp-functions.mdx
index 09693d8e25d..17e06ad027e 100644
--- a/docs/source/deployment/gcp-functions.mdx
+++ b/docs/source/deployment/gcp-functions.mdx
@@ -27,6 +27,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
});
exports.handler = server.createHandler();
@@ -164,6 +165,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
context: ({ req, res }) => ({
headers: req.headers,
req,
diff --git a/docs/source/deployment/lambda.md b/docs/source/deployment/lambda.md
index 2e2c0011823..a6550ec0aed 100644
--- a/docs/source/deployment/lambda.md
+++ b/docs/source/deployment/lambda.md
@@ -50,7 +50,12 @@ const resolvers = {
},
};
-const server = new ApolloServer({ typeDefs, resolvers, csrfPrevention: true });
+const server = new ApolloServer({
+ typeDefs,
+ resolvers,
+ csrfPrevention: true,
+ cache: 'bounded',
+});
exports.graphqlHandler = server.createHandler();
```
@@ -211,6 +216,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
context: ({ event, context, express }) => ({
headers: event.headers,
functionName: context.functionName,
diff --git a/docs/source/getting-started.mdx b/docs/source/getting-started.mdx
index 6f9a96ee406..1562ca8f4fe 100644
--- a/docs/source/getting-started.mdx
+++ b/docs/source/getting-started.mdx
@@ -152,6 +152,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
});
// The `listen` method launches a web server.
diff --git a/docs/source/integrations/middleware.mdx b/docs/source/integrations/middleware.mdx
index ef9003a9121..b9a4eab6a49 100644
--- a/docs/source/integrations/middleware.mdx
+++ b/docs/source/integrations/middleware.mdx
@@ -130,7 +130,12 @@ Let's say our `apollo-server` implementation uses the following code:
import { ApolloServer } from "apollo-server";
async function startApolloServer(typeDefs, resolvers) {
- const server = new ApolloServer({ typeDefs, resolvers, csrfPrevention: true });
+ const server = new ApolloServer({
+ typeDefs,
+ resolvers,
+ csrfPrevention: true,
+ cache: "bounded",
+ });
const { url } = await server.listen();
console.log(`🚀 Server ready at ${url}`);
}
@@ -166,6 +171,7 @@ async function startApolloServer(typeDefs, resolvers) {
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});
@@ -228,7 +234,12 @@ npm install apollo-server graphql
import { ApolloServer } from 'apollo-server';
async function startApolloServer(typeDefs, resolvers) {
- const server = new ApolloServer({ typeDefs, resolvers, csrfPrevention: true });
+ const server = new ApolloServer({
+ typeDefs,
+ resolvers,
+ csrfPrevention: true,
+ cache: 'bounded',
+ });
const { url } = await server.listen();
console.log(`🚀 Server ready at ${url}`);
}
@@ -267,6 +278,7 @@ async function startApolloServer(typeDefs, resolvers) {
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});
await server.start();
@@ -326,6 +338,7 @@ async function startApolloServer(typeDefs, resolvers) {
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
plugins: [
fastifyAppClosePlugin(app),
ApolloServerPluginDrainHttpServer({ httpServer: app.server }),
@@ -377,6 +390,7 @@ async function startApolloServer(typeDefs, resolvers) {
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
plugins: [ApolloServerPluginStopHapiServer({ hapiServer: app })],
});
@@ -423,6 +437,7 @@ async function startApolloServer(typeDefs, resolvers) {
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});
@@ -466,7 +481,12 @@ npm install apollo-server-micro micro graphql
```ts title="index.ts"
import { ApolloServer } from 'apollo-server-micro';
-const server = new ApolloServer({ typeDefs, resolvers, csrfPrevention: true });
+const server = new ApolloServer({
+ typeDefs,
+ resolvers,
+ csrfPrevention: true,
+ cache: 'bounded',
+});
module.exports = server.start().then(() => server.createHandler());
```
@@ -503,7 +523,12 @@ npm install apollo-server-lambda graphql
```ts title="index.ts"
import { ApolloServer } from 'apollo-server-lambda';
-const server = new ApolloServer({ typeDefs, resolvers, csrfPrevention: true });
+const server = new ApolloServer({
+ typeDefs,
+ resolvers,
+ csrfPrevention: true,
+ cache: 'bounded',
+});
exports.handler = server.createHandler();
```
@@ -529,7 +554,12 @@ npm install apollo-server-cloud-functions graphql
```ts title="index.ts"
import { ApolloServer } from 'apollo-server-cloud-functions';
-const server = new ApolloServer({ typeDefs, resolvers, csrfPrevention: true });
+const server = new ApolloServer({
+ typeDefs,
+ resolvers,
+ csrfPrevention: true,
+ cache: 'bounded',
+});
exports.handler = server.createHandler();
```
@@ -554,7 +584,12 @@ npm install apollo-server-azure-functions graphql
```ts title="index.ts"
import { ApolloServer } from 'apollo-server-azure-functions';
-const server = new ApolloServer({ typeDefs, resolvers, csrfPrevention: true });
+const server = new ApolloServer({
+ typeDefs,
+ resolvers,
+ csrfPrevention: true,
+ cache: 'bounded',
+});
exports.handler = server.createHandler();
```
diff --git a/docs/source/integrations/plugins.md b/docs/source/integrations/plugins.md
index 547caafc255..02aed5bf63d 100644
--- a/docs/source/integrations/plugins.md
+++ b/docs/source/integrations/plugins.md
@@ -207,6 +207,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
// You can import plugins or define them in-line, as shown:
plugins: [
diff --git a/docs/source/monitoring/health-checks.md b/docs/source/monitoring/health-checks.md
index b4a3a9929b6..a9d46903e4a 100644
--- a/docs/source/monitoring/health-checks.md
+++ b/docs/source/monitoring/health-checks.md
@@ -29,7 +29,7 @@ You can pass a string `healthCheckPath` to the `ApolloServer` constructor to cha
If you'd like the health check to do more than just "always return success", you can pass an async function `onHealthCheck` function to the `ApolloServer` constructor. If defined, this `onHealthCheck` async function should return if the server is deemed _ready_ or `throw` if there is an error. Returning (resolving the `Promise`) will result in an HTTP status code of 200, which is generally desired by most health-check tooling (e.g. Kubernetes, AWS, etc.), while `throw`ing (rejecting the `Promise`) will result in an HTTP status code of 503.
-```js {10-17}
+```js {10-18}
import { ApolloServer, gql } from 'apollo-server';
// Undefined for brevity.
@@ -40,6 +40,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
async onHealthCheck() {
if (everythingLooksHealthy()) {
return;
diff --git a/docs/source/monitoring/metrics.md b/docs/source/monitoring/metrics.md
index 9dda6ba0554..321c4878843 100644
--- a/docs/source/monitoring/metrics.md
+++ b/docs/source/monitoring/metrics.md
@@ -55,7 +55,7 @@ version in the [`ApolloClient` constructor](https://www.apollographql.com/docs/r
For more advanced cases, or to use headers other than the default headers, pass a `generateClientInfo` function into the [usage reporting plugin](../api/plugin/usage-reporting/):
-```js {9-24}
+```js {10-25}
const { ApolloServer } = require("apollo-server");
const { ApolloServerPluginUsageReporting } = require("apollo-server-core");
@@ -63,6 +63,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: "bounded",
plugins: [
ApolloServerPluginUsageReporting({
generateClientInfo: ({
@@ -71,8 +72,8 @@ const server = new ApolloServer({
const headers = request.http && request.http.headers;
if(headers) {
return {
- clientName: headers['apollographql-client-name'],
- clientVersion: headers['apollographql-client-version'],
+ clientName: headers["apollographql-client-name"],
+ clientVersion: headers["apollographql-client-version"],
};
} else {
return {
@@ -126,6 +127,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
plugins: [
myPlugin
]
diff --git a/docs/source/performance/apq.md b/docs/source/performance/apq.md
index 56248efbeec..dd528f8b2de 100644
--- a/docs/source/performance/apq.md
+++ b/docs/source/performance/apq.md
@@ -134,6 +134,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
// The max age is calculated in seconds
plugins: [ApolloServerPluginCacheControl({ defaultMaxAge: 5 })],
});
@@ -206,6 +207,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
// highlight-start
persistedQueries: {
cache: new MemcachedCache(
@@ -231,6 +233,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
// highlight-start
persistedQueries: {
cache: new BaseRedisCache({
@@ -257,6 +260,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
// highlight-start
persistedQueries: {
cache: new BaseRedisCache({
@@ -288,6 +292,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
// highlight-start
persistedQueries: {
cache: new BaseRedisCache({
@@ -318,6 +323,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
persistedQueries: {
// highlight-start
ttl: 900, // 15 minutes
@@ -333,6 +339,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
persistedQueries: {
ttl: null, // highlight-line
},
@@ -350,6 +357,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
persistedQueries: false, // highlight-line
});
```
diff --git a/docs/source/proxy-configuration.md b/docs/source/proxy-configuration.md
index dccced5aad8..c5cee22349f 100644
--- a/docs/source/proxy-configuration.md
+++ b/docs/source/proxy-configuration.md
@@ -38,6 +38,7 @@ const server = new ApolloServer({
typesDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
});
```
diff --git a/docs/source/schema/creating-directives.mdx b/docs/source/schema/creating-directives.mdx
index 776c68b8764..e7ea3f452ba 100644
--- a/docs/source/schema/creating-directives.mdx
+++ b/docs/source/schema/creating-directives.mdx
@@ -338,7 +338,11 @@ let schema = makeExecutableSchema({
schema = upperDirectiveTransformer(schema, 'upper');
// Provide the schema to the ApolloServer constructor
-const server = new ApolloServer({schema, csrfPrevention: true});
+const server = new ApolloServer({
+ schema,
+ csrfPrevention: true,
+ cache: 'bounded',
+});
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
diff --git a/docs/source/schema/custom-scalars.md b/docs/source/schema/custom-scalars.md
index b55b8b80028..ff24b59907f 100644
--- a/docs/source/schema/custom-scalars.md
+++ b/docs/source/schema/custom-scalars.md
@@ -116,6 +116,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
});
```
@@ -139,10 +140,10 @@ const typeDefs = gql`
// Validation function for checking "oddness"
function oddValue(value) {
- if (typeof value === "number" && Number.isInteger(value) && value % 2 !== 0) {
+ if (typeof value === 'number' && Number.isInteger(value) && value % 2 !== 0) {
return value;
}
- throw new UserInputError("Provided value is not an odd integer");
+ throw new UserInputError('Provided value is not an odd integer');
}
const resolvers = {
@@ -155,7 +156,7 @@ const resolvers = {
if (ast.kind === Kind.INT) {
return oddValue(parseInt(ast.value, 10));
}
- throw new UserInputError("Provided value is not an odd integer");
+ throw new UserInputError('Provided value is not an odd integer');
},
}),
Query: {
@@ -165,7 +166,12 @@ const resolvers = {
}
};
-const server = new ApolloServer({ typeDefs, resolvers, csrfPrevention: true });
+const server = new ApolloServer({
+ typeDefs,
+ resolvers,
+ csrfPrevention: true,
+ cache: 'bounded',
+});
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`)
@@ -207,7 +213,12 @@ const resolvers = {
// ...other resolvers...
};
-const server = new ApolloServer({ typeDefs, resolvers, csrfPrevention: true });
+const server = new ApolloServer({
+ typeDefs,
+ resolvers,
+ csrfPrevention: true,
+ cache: 'bounded',
+});
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`)
diff --git a/docs/source/schema/unions-interfaces.md b/docs/source/schema/unions-interfaces.md
index 762e4482a37..6685c4bb2dc 100644
--- a/docs/source/schema/unions-interfaces.md
+++ b/docs/source/schema/unions-interfaces.md
@@ -132,6 +132,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
});
server.listen().then(({ url }) => {
diff --git a/docs/source/security/authentication.md b/docs/source/security/authentication.md
index f3a07969ddd..0dcf124aa0a 100644
--- a/docs/source/security/authentication.md
+++ b/docs/source/security/authentication.md
@@ -21,6 +21,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
+ cache: 'bounded',
context: ({ req }) => {
// Note: This example uses the `req` argument to access headers,
// but the arguments received by `context` vary by integration.
@@ -229,7 +230,8 @@ One way of implementing the `@auth` directive is by constructing your schema usi
new ApolloServer({
schema: makeExecutableSchema({ typeDefs, resolvers, schemaTransforms }),
csrfPrevention: true,
-})
+ cache: 'bounded',
+});
```
### Outside of GraphQL
diff --git a/docs/source/security/cors.mdx b/docs/source/security/cors.mdx
index 3af40701e36..4cc05158053 100644
--- a/docs/source/security/cors.mdx
+++ b/docs/source/security/cors.mdx
@@ -99,6 +99,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true, // see below for more about this
+ cache: "bounded",
cors: {
origin: ["https://www.your-app.example", "https://studio.apollographql.com"]
},
@@ -145,6 +146,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true, // see below for more about this
+ cache: "bounded",
cors: {
origin: yourOrigin,
credentials: true
diff --git a/docs/source/security/terminating-ssl.mdx b/docs/source/security/terminating-ssl.mdx
index 1b4bc4130b2..98d706d240a 100644
--- a/docs/source/security/terminating-ssl.mdx
+++ b/docs/source/security/terminating-ssl.mdx
@@ -30,7 +30,12 @@ async function startApolloServer() {
const environment = process.env.NODE_ENV || 'production';
const config = configurations[environment];
- const server = new ApolloServer({ typeDefs, resolvers, csrfPrevention: true });
+ const server = new ApolloServer({
+ typeDefs,
+ resolvers,
+ csrfPrevention: true,
+ cache: 'bounded',
+ });
await server.start();
const app = express();
diff --git a/packages/apollo-server-core/src/ApolloServer.ts b/packages/apollo-server-core/src/ApolloServer.ts
index 1736f53a828..154cdef924e 100644
--- a/packages/apollo-server-core/src/ApolloServer.ts
+++ b/packages/apollo-server-core/src/ApolloServer.ts
@@ -261,10 +261,14 @@ export class ApolloServerBase<
: noIntro;
}
- if (!requestOptions.cache) {
+ if (requestOptions.cache === 'bounded') {
requestOptions.cache = new InMemoryLRUCache();
}
+ if (!requestOptions.cache) {
+ requestOptions.cache = new UnboundedCache();
+ }
+
if (requestOptions.persistedQueries !== false) {
const { cache: apqCache = requestOptions.cache!, ...apqOtherOptions } =
requestOptions.persistedQueries || Object.create(null);
diff --git a/packages/apollo-server-core/src/types.ts b/packages/apollo-server-core/src/types.ts
index db3fa2244b2..37a0825da41 100644
--- a/packages/apollo-server-core/src/types.ts
+++ b/packages/apollo-server-core/src/types.ts
@@ -40,7 +40,6 @@ type BaseConfig = Pick<
| 'formatResponse'
| 'fieldResolver'
| 'dataSources'
- | 'cache'
| 'logger'
| 'allowBatchedHttpRequests'
>;
@@ -110,6 +109,7 @@ export interface Config extends BaseConfig {
nodeEnv?: string;
documentStore?: DocumentStore | null;
csrfPrevention?: CSRFPreventionOptions | boolean;
+ cache?: KeyValueCache | 'bounded';
}
export interface CSRFPreventionOptions {
diff --git a/packages/apollo-server-integration-testsuite/src/ApolloServer.ts b/packages/apollo-server-integration-testsuite/src/ApolloServer.ts
index 6ba585f4bb9..90aedbaef29 100644
--- a/packages/apollo-server-integration-testsuite/src/ApolloServer.ts
+++ b/packages/apollo-server-integration-testsuite/src/ApolloServer.ts
@@ -59,8 +59,8 @@ import FakeTimers from '@sinonjs/fake-timers';
import type { AddressInfo } from 'net';
import request, { Response } from 'supertest';
import {
- type KeyValueCache,
InMemoryLRUCache,
+ type KeyValueCache,
} from '@apollo/utils.keyvaluecache';
const quietLogger = loglevel.getLogger('quiet');
@@ -2265,45 +2265,38 @@ export function testApolloServer(
clock.uninstall();
});
- it('basic caching', async () => {
- class TTLTestingCache implements KeyValueCache {
- private fakeTime = 0;
- constructor(
- private cache: Map<
- string,
- { value: string; deadline: number | null }
- > = new Map(),
- ) {}
-
- async get(key: string) {
- const entry = this.cache.get(key);
- if (!entry) return undefined;
- if (entry.deadline && entry.deadline <= this.fakeTime) {
- await this.delete(key);
- return undefined;
- }
- return entry.value;
- }
+ it('uses an unbounded cache by default', async () => {
+ const server = new ApolloServerBase({
+ typeDefs: `type Query { hello: String }`,
+ });
- async set(
- key: string,
- value: string,
- { ttl }: { ttl: number | null } = { ttl: null },
- ) {
- this.cache.set(key, {
- value,
- deadline: ttl ? this.fakeTime + ttl * 1000 : null,
- });
- }
+ // This could be an instanceof check but we don't really want to export
+ // the `UnboundedCache` class from `apollo-server-core`
+ expect(server['requestOptions'].cache!.constructor.name).toBe(
+ 'UnboundedCache',
+ );
+ });
- async delete(key: string) {
- this.cache.delete(key);
- }
+ it('uses a bounded cache', async () => {
+ const server = new ApolloServerBase({
+ typeDefs: `type Query { hello: String }`,
+ cache: 'bounded',
+ });
- advanceTime(ms: number) {
- this.fakeTime += ms;
- }
- }
+ expect(server['requestOptions'].cache).toBeInstanceOf(InMemoryLRUCache);
+ });
+
+ it('uses a custom cache', async () => {
+ const customCache = {} as KeyValueCache;
+ const server = new ApolloServerBase({
+ typeDefs: `type Query { hello: String }`,
+ cache: customCache,
+ });
+
+ expect(server['requestOptions'].cache).toBe(customCache);
+ });
+
+ it('basic caching', async () => {
const typeDefs = gql`
type Query {
cached: String @cacheControl(maxAge: 10)
@@ -2359,12 +2352,9 @@ export function testApolloServer(
};
});
- const fakeTTLCache = new TTLTestingCache();
-
const { url: uri } = await createApolloServer({
typeDefs,
resolvers,
- cache: fakeTTLCache,
plugins: [
ApolloServerPluginResponseCache({
sessionId: (requestContext: GraphQLRequestContext) => {
@@ -2488,7 +2478,6 @@ export function testApolloServer(
}
// Cache hit partway to ttl.
- fakeTTLCache.advanceTime(5 * 1000);
clock.tick(5 * 1000);
{
const result = await fetch();
@@ -2500,7 +2489,6 @@ export function testApolloServer(
}
// Cache miss after ttl.
- fakeTTLCache.advanceTime(6 * 1000);
clock.tick(6 * 1000);
{
const result = await fetch();
@@ -2778,7 +2766,6 @@ export function testApolloServer(
}
// Let's expire the cache, and run again, not writing to the cache.
- fakeTTLCache.advanceTime(15 * 1000);
clock.tick(15 * 1000);
{
const result = await doFetch({
|