Skip to content
This repository has been archived by the owner on Mar 20, 2023. It is now read-only.

Allow to pass function that returns Promise as a rootValue option #253

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Expand Up @@ -41,7 +41,8 @@ The `graphqlHTTP` function accepts the following options:
quite useful. You may or may not want it in production.

* **`rootValue`**: A value to pass as the `rootValue` to the `graphql()`
function from [`GraphQL.js`][].
function from [`GraphQL.js`][]. Can be provided as an object, a Promise for
an object, or a Function that returns an object or a Promise for an object.

* **`context`**: A value to pass as the `context` to the `graphql()`
function from [`GraphQL.js`][]. If `context` is not provided, the
Expand Down
73 changes: 73 additions & 0 deletions src/__tests__/http-test.js
Expand Up @@ -63,6 +63,12 @@ const QueryRootType = new GraphQLObjectType({
return (context: any).foo;
},
},
rootValueField: {
type: GraphQLString,
resolve: obj => {
return (obj: any).rootValueField;
},
}
}
});

Expand Down Expand Up @@ -1577,5 +1583,72 @@ describe('test harness', () => {
);
});
});


describe('Custom rootValue', () => {

it('allows to supply rootValue as an object', async () => {
const app = server();

app.use(urlString(), graphqlHTTP({
schema: TestSchema,
rootValue: {rootValueField: 'Test Value'},
}));

const response = await request(app)
.get(urlString({ query: '{rootValueField}', raw: '' }))
.set('Accept', 'text/html');

expect(response.status).to.equal(200);
expect(response.type).to.equal('application/json');
expect(response.text).to.equal(
'{"data":{"rootValueField":"Test Value"}}'
);
});

it('allows to supply rootValue as a Promise for an object', async () => {
const app = server();

app.use(urlString(), graphqlHTTP({
schema: TestSchema,
rootValue: new Promise(resolve => resolve({
rootValueField: 'Test Value'
})),
}));

const response = await request(app)
.get(urlString({ query: '{rootValueField}', raw: '' }))
.set('Accept', 'text/html');

expect(response.status).to.equal(200);
expect(response.type).to.equal('application/json');
expect(response.text).to.equal(
'{"data":{"rootValueField":"Test Value"}}'
);
});

it('allows to supply rootValue as Function that returns a Promise for an object', async () => {
const app = server();

app.use(urlString(), graphqlHTTP({
schema: TestSchema,
rootValue() {
return new Promise(resolve => resolve({
rootValueField: 'Test Value'
}));
}
}));

const response = await request(app)
.get(urlString({query: '{rootValueField}', raw: ''}))
.set('Accept', 'text/html');

expect(response.status).to.equal(200);
expect(response.type).to.equal('application/json');
expect(response.text).to.equal(
'{"data":{"rootValueField":"Test Value"}}'
);
});
});
});
});
52 changes: 37 additions & 15 deletions src/index.js
Expand Up @@ -63,8 +63,10 @@ export type OptionsData = {

/**
* An object to pass as the rootValue to the graphql() function.
* Can be provided as an object, a Promise for an object, or a Function that
* returns an object or a Promise for an object.
*/
rootValue?: ?mixed,
rootValue?: ?RootValue,

/**
* A boolean to configure whether the output should be pretty-printed.
Expand Down Expand Up @@ -102,6 +104,13 @@ export type OptionsData = {
graphiql?: ?boolean,
};

/**
* RootValue can be provided as an object, a Promise for an object,
* or a Function that returns an object or a Promise for an object.
*/
export type RootValue =
((info: RequestInfo) => mixed) | mixed;

/**
* All information about a GraphQL request.
*/
Expand Down Expand Up @@ -261,21 +270,34 @@ function graphqlHTTP(options: Options): Middleware {
);
}
}
// Perform the execution, reporting any errors creating the context.
try {
return execute(
schema,
documentAST,
rootValue,
context,
variables,
operationName

return new Promise(resolve => {
resolve(
typeof rootValue === 'function' ?
rootValue({
document: documentAST,
variables,
operationName,
result: null,
}) : rootValue
);
} catch (contextError) {
// Return 400: Bad Request if any execution context errors exist.
response.statusCode = 400;
return { errors: [ contextError ] };
}
}).then(rootValueData => {
// Perform the execution, reporting any errors creating the context.
try {
return execute(
schema,
documentAST,
rootValueData,
context,
variables,
operationName
);
} catch (contextError) {
// Return 400: Bad Request if any execution context errors exist.
response.statusCode = 400;
return { errors: [ contextError ] };
}
});
}).then(result => {
// Collect and apply any metadata extensions if a function was provided.
// http://facebook.github.io/graphql/#sec-Response-Format
Expand Down