Skip to content

Commit

Permalink
Allow an optional function to resolve the rootValue
Browse files Browse the repository at this point in the history
Passes the parsed DocumentNode AST to determine the root value,
useful when providing a different rootValue for query vs mutation
  • Loading branch information
tgriesser committed Aug 20, 2018
1 parent dca3ae4 commit 75d83b8
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -5,6 +5,7 @@ All of the packages in the `apollo-server` repo are released with the same versi
### vNEXT

- Google Cloud Function support [#1402](https://github.com/apollographql/apollo-server/issues/1402)
- Allow an optional function to resolve the `rootValue`, passing the `DocumentNode` AST to determine the value.

### v2.0.4

Expand Down
17 changes: 17 additions & 0 deletions packages/apollo-server-core/src/__tests__/runQuery.test.ts
Expand Up @@ -8,6 +8,7 @@ import {
GraphQLInt,
GraphQLNonNull,
parse,
DocumentNode,
} from 'graphql';

import { runQuery } from '../runQuery';
Expand Down Expand Up @@ -175,6 +176,22 @@ describe('runQuery', () => {
});
});

it('correctly evaluates a rootValue function', () => {
const query = `{ testRootValue }`;
const expected = { testRootValue: 'it also works' };
return runQuery({
schema,
queryString: query,
rootValue: (doc: DocumentNode) => {
expect(doc.kind).toEqual('Document');
return 'it also';
},
request: new MockReq(),
}).then(res => {
expect(res.data).toEqual(expected);
});
});

it('correctly passes in the context', () => {
const query = `{ testContextValue }`;
const expected = { testContextValue: 'it still works' };
Expand Down
8 changes: 5 additions & 3 deletions packages/apollo-server-core/src/graphqlOptions.ts
Expand Up @@ -2,6 +2,7 @@ import {
GraphQLSchema,
ValidationContext,
GraphQLFieldResolver,
DocumentNode,
} from 'graphql';
import { GraphQLExtension } from 'graphql-extensions';
import { CacheControlExtensionOptions } from 'apollo-cache-control';
Expand All @@ -13,7 +14,7 @@ import { DataSource } from 'apollo-datasource';
*
* - schema: an executable GraphQL schema used to fulfill requests.
* - (optional) formatError: Formatting function applied to all errors before response is sent
* - (optional) rootValue: rootValue passed to GraphQL execution
* - (optional) rootValue: rootValue passed to GraphQL execution, or a function to resolving the rootValue from the DocumentNode
* - (optional) context: the context passed to GraphQL execution
* - (optional) validationRules: extra validation rules applied to requests
* - (optional) formatResponse: a function applied to each graphQL execution result
Expand All @@ -25,11 +26,12 @@ import { DataSource } from 'apollo-datasource';
export interface GraphQLServerOptions<
TContext =
| (() => Promise<Record<string, any>> | Record<string, any>)
| Record<string, any>
| Record<string, any>,
TRootVal = ((parsedQuery: DocumentNode) => any) | any
> {
schema: GraphQLSchema;
formatError?: Function;
rootValue?: any;
rootValue?: TRootVal;
context?: TContext;
validationRules?: Array<(context: ValidationContext) => any>;
formatResponse?: Function;
Expand Down
7 changes: 5 additions & 2 deletions packages/apollo-server-core/src/runQuery.ts
Expand Up @@ -50,7 +50,7 @@ export interface QueryOptions {
// a mutation), throw this error.
nonQueryError?: Error;

rootValue?: any;
rootValue?: ((parsedQuery: DocumentNode) => any) | any;
context?: any;
variables?: { [key: string]: any };
operationName?: string;
Expand Down Expand Up @@ -219,7 +219,10 @@ function doRunQuery(options: QueryOptions): Promise<GraphQLResponse> {
const executionArgs: ExecutionArgs = {
schema: options.schema,
document: documentAST,
rootValue: options.rootValue,
rootValue:
typeof options.rootValue === 'function'
? options.rootValue(documentAST)
: options.rootValue,
contextValue: context,
variableValues: options.variables,
operationName: options.operationName,
Expand Down
36 changes: 36 additions & 0 deletions packages/apollo-server-integration-testsuite/src/index.ts
Expand Up @@ -12,6 +12,8 @@ import {
GraphQLScalarType,
introspectionQuery,
BREAK,
DocumentNode,
getOperationAST,
} from 'graphql';

import request = require('supertest');
Expand Down Expand Up @@ -842,6 +844,40 @@ export default (createApp: CreateAppFunc, destroyApp?: DestroyAppFunc) => {
});
});

it('passes the rootValue function result to the resolver', async () => {
const expectedQuery = 'query: it passes rootValue';
const expectedMutation = 'mutation: it passes rootValue';
app = await createApp({
graphqlOptions: {
schema,
rootValue: (documentNode: DocumentNode) => {
const op = getOperationAST(documentNode, undefined);
return op.operation === 'query'
? expectedQuery
: expectedMutation;
},
},
});
const queryReq = request(app)
.post('/graphql')
.send({
query: 'query test{ testRootValue }',
});
return queryReq.then(res => {
expect(res.status).toEqual(200);
expect(res.body.data.testRootValue).toEqual(expectedQuery);
});
const mutationReq = request(app)
.post('/graphql')
.send({
query: 'mutation test{ testMutation(echo: "ping") }',
});
return mutationReq.then(res => {
expect(res.status).toEqual(200);
expect(res.body.data.testRootValue).toEqual(expectedMutation);
});
});

it('returns errors', async () => {
const expected = 'Secret error message';
app = await createApp({
Expand Down

0 comments on commit 75d83b8

Please sign in to comment.