Skip to content

Commit

Permalink
Full query caching
Browse files Browse the repository at this point in the history
  • Loading branch information
glasser committed Mar 12, 2019
1 parent 9d39f2c commit 84141bd
Show file tree
Hide file tree
Showing 21 changed files with 436 additions and 25 deletions.
8 changes: 8 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"apollo-server-lambda": "file:packages/apollo-server-lambda",
"apollo-server-micro": "file:packages/apollo-server-micro",
"apollo-server-plugin-base": "file:packages/apollo-server-plugin-base",
"apollo-server-plugin-full-query-cache": "file:packages/apollo-server-plugin-full-query-cache",
"apollo-server-testing": "file:packages/apollo-server-testing",
"apollo-tracing": "file:packages/apollo-tracing",
"graphql-extensions": "file:packages/graphql-extensions"
Expand Down
1 change: 1 addition & 0 deletions packages/apollo-cache-control/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"node": ">=6.0"
},
"dependencies": {
"apollo-server-env": "file:../apollo-server-core",
"apollo-server-env": "file:../apollo-server-env",
"graphql-extensions": "file:../graphql-extensions"
},
Expand Down
16 changes: 6 additions & 10 deletions packages/apollo-cache-control/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,17 @@ import {

import { GraphQLExtension, GraphQLResponse } from 'graphql-extensions';

import {
CacheHint,
CacheScope,
} from 'apollo-server-core/dist/requestPipelineAPI';
export { CacheHint, CacheScope };

export interface CacheControlFormat {
version: 1;
hints: ({ path: (string | number)[] } & CacheHint)[];
}

export interface CacheHint {
maxAge?: number;
scope?: CacheScope;
}

export enum CacheScope {
Public = 'PUBLIC',
Private = 'PRIVATE',
}

export interface CacheControlExtensionOptions {
defaultMaxAge?: number;
// FIXME: We should replace these with
Expand Down
1 change: 1 addition & 0 deletions packages/apollo-cache-control/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"include": ["src/**/*"],
"exclude": ["**/__tests__", "**/__mocks__"],
"references": [
{ "path": "../apollo-server-core/tsconfig.requestPipelineAPI.json" },
{ "path": "../graphql-extensions" }
]
}
1 change: 1 addition & 0 deletions packages/apollo-server-caching/src/KeyValueCache.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export interface KeyValueCache<V = string> {
get(key: string): Promise<V | undefined>;
// ttl is measured in seconds.
set(key: string, value: V, options?: { ttl?: number }): Promise<void>;
delete(key: string): Promise<boolean | void>;
}
Expand Down
49 changes: 35 additions & 14 deletions packages/apollo-server-core/src/requestPipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,26 +290,47 @@ export async function processGraphQLRequest<TContext>(
);
}

const executionDidEnd = await dispatcher.invokeDidStartHook(
'executionDidStart',
let response: GraphQLResponse;

// Ask plugins (eg, full query cache) for responses.
const pluginGraphQLResponses = (await dispatcher.invokeHookAsync(
'execute',
requestContext as WithRequired<
typeof requestContext,
'document' | 'operation' | 'operationName'
>,
);
)).filter(x => x);
if (pluginGraphQLResponses.length > 1) {
// XXX how hard would it be to make this error message more actionable?
// We'd need to know plugin names...
throw new Error('Multiple plugins provided a direct execution result');
}
if (pluginGraphQLResponses.length > 0) {
response = pluginGraphQLResponses[0]!;
} else {
const executionDidEnd = await dispatcher.invokeDidStartHook(
'executionDidStart',
requestContext as WithRequired<
typeof requestContext,
'document' | 'operation' | 'operationName'
>,
);

let response: GraphQLResponse;
try {
response = (await execute(
requestContext.document,
request.operationName,
request.variables,
)) as GraphQLResponse;
executionDidEnd();
} catch (executionError) {
executionDidEnd(executionError);
return sendErrorResponse(executionError);
}
}

try {
response = (await execute(
requestContext.document,
request.operationName,
request.variables,
)) as GraphQLResponse;
executionDidEnd();
} catch (executionError) {
executionDidEnd(executionError);
return sendErrorResponse(executionError);
if (cacheControlExtension) {
requestContext.overallCachePolicy = cacheControlExtension.computeOverallCachePolicy();
}

const formattedExtensions = extensionStack.format();
Expand Down
12 changes: 12 additions & 0 deletions packages/apollo-server-core/src/requestPipelineAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ import {
} from 'graphql';
import { KeyValueCache } from 'apollo-server-caching';

export interface CacheHint {
maxAge?: number;
scope?: CacheScope;
}

export enum CacheScope {
Public = 'PUBLIC',
Private = 'PRIVATE',
}

export interface GraphQLServiceContext {
schema: GraphQLSchema;
schemaHash: string;
Expand Down Expand Up @@ -57,6 +67,8 @@ export interface GraphQLRequestContext<TContext = Record<string, any>> {
readonly operationName?: string | null;
readonly operation?: OperationDefinitionNode;

readonly overallCachePolicy?: Required<CacheHint> | undefined;

debug?: boolean;
}

Expand Down
13 changes: 12 additions & 1 deletion packages/apollo-server-plugin-base/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,23 @@ export interface GraphQLRequestListener<TContext = Record<string, any>> {
'document' | 'operationName' | 'operation'
>,
): ValueOrPromise<void>;
// If this hook is defined, it is invoked immediately before GraphQL execution
// would take place. If its return value resolves to a non-null
// GraphQLResponse, that result is used instead of executing the query. It is
// an error for more than one plugin to return a value that resolves to a
// non-null GraphQLResponse.
execute?(
requestContext: WithRequired<
GraphQLRequestContext<TContext>,
'document' | 'operationName' | 'operation'
>,
): ValueOrPromise<GraphQLResponse | null>;
executionDidStart?(
requestContext: WithRequired<
GraphQLRequestContext<TContext>,
'document' | 'operationName' | 'operation'
>,
): (err?: Error) => void | void;
): ((err?: Error) => void) | void;
willSendResponse?(
requestContext: WithRequired<GraphQLRequestContext<TContext>, 'response'>,
): ValueOrPromise<void>;
Expand Down
6 changes: 6 additions & 0 deletions packages/apollo-server-plugin-full-query-cache/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*
!src/**/*
!dist/**/*
dist/**/*.test.*
!package.json
!README.md
4 changes: 4 additions & 0 deletions packages/apollo-server-plugin-full-query-cache/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Change Log

### vNEXT

12 changes: 12 additions & 0 deletions packages/apollo-server-plugin-full-query-cache/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Full Query Cache plugin

This Apollo server plugin implements a full GraphQL query cache.

- Add the plugin to your ApolloServer's plugins list
- Set `@cacheControl` hints on your schema or call `info.cacheControl.setCacheHint` in your resolvers
- If the entire GraphQL response is covered by cache hints with non-zero maxAge,
the whole response will be cached.

This cache is a full query cache: cached responses are only used for identical requests.


3 changes: 3 additions & 0 deletions packages/apollo-server-plugin-full-query-cache/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const config = require('../../jest.config.base');

module.exports = Object.assign(Object.create(null), config);
29 changes: 29 additions & 0 deletions packages/apollo-server-plugin-full-query-cache/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "apollo-server-plugin-full-query-cache",
"version": "0.0.0-alpha.1",
"description": "Apollo Server full query cache",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/apollographql/apollo-server/tree/master/packages/apollo-server-plugin-full-query-cache"
},
"keywords": [],
"author": "Apollo <npm@apollographql.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/apollographql/apollo-server/issues"
},
"homepage": "https://github.com/apollographql/apollo-server#readme",
"engines": {
"node": ">=6"
},
"dependencies": {
"apollo-server-caching": "file:../apollo-server-caching",
"apollo-server-plugin-base": "file:../apollo-server-plugin-base",
"apollo-server-env": "file:../apollo-server-env"
},
"peerDependencies": {
"graphql": "^0.12.0 || ^0.13.0 || ^14.0.0"
}
}
Loading

0 comments on commit 84141bd

Please sign in to comment.