diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b710567df8..081e5855f02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ### VNEXT +* run batched requests in parallel ([@DxCx](https://github.com/DxCx)) on [#273](https://github.com/apollostack/graphql-server/pull/273) * Fix GraphiQL options variables. Issue #193. ([@alanchristensen](https://github.com/alanchristensen)) on [PR #255](https://github.com/apollostack/apollo-server/pull/255) diff --git a/packages/graphql-server-core/src/runHttpQuery.ts b/packages/graphql-server-core/src/runHttpQuery.ts index aaf6fe79520..efef17e71e1 100644 --- a/packages/graphql-server-core/src/runHttpQuery.ts +++ b/packages/graphql-server-core/src/runHttpQuery.ts @@ -74,8 +74,7 @@ export async function runHttpQuery(handlerArguments: Array, request: HttpQu requestPayload = [requestPayload]; } - let responses: Array = []; - for (let requestParams of requestPayload) { + const requests: Array = requestPayload.map(requestParams => { try { let query = requestParams.query; if ( isGetRequest ) { @@ -128,17 +127,18 @@ export async function runHttpQuery(handlerArguments: Array, request: HttpQu params = optionsObject.formatParams(params); } - responses.push(await runQuery(params)); + return runQuery(params); } catch (e) { // Populate any HttpQueryError to our handler which should // convert it to Http Error. if ( e.name === 'HttpQueryError' ) { - throw e; + return Promise.reject(e); } - responses.push({ errors: [formatErrorFn(e)] }); + return Promise.resolve({ errors: [formatErrorFn(e)] }); } - } + }); + const responses = await Promise.all(requests); if (!isBatch) { const gqlResponse = responses[0]; diff --git a/packages/graphql-server-express/src/apolloServerHttp.test.ts b/packages/graphql-server-express/src/apolloServerHttp.test.ts index 03528cac3bf..50e3658a7ac 100644 --- a/packages/graphql-server-express/src/apolloServerHttp.test.ts +++ b/packages/graphql-server-express/src/apolloServerHttp.test.ts @@ -154,7 +154,9 @@ const version = 'modern'; describe(`GraphQL-HTTP (apolloServer) tests for ${version} express`, () => { describe('POST functionality', () => { - it('allows gzipped POST bodies', async () => { + it('allows gzipped POST bodies', async function () { + // Increase timeout for slow node 4 + this.timeout(3000); const app = express(); app.use(urlString(), bodyParser.json()); @@ -181,7 +183,9 @@ describe(`GraphQL-HTTP (apolloServer) tests for ${version} express`, () => { }); }); - it('allows deflated POST bodies', async () => { + it('allows deflated POST bodies', async function () { + // Increase timeout for slow node 4 + this.timeout(3000); const app = express(); app.use(urlString(), bodyParser.json()); diff --git a/packages/graphql-server-integration-testsuite/src/index.ts b/packages/graphql-server-integration-testsuite/src/index.ts index f87f6834c59..f48dc6c1cc7 100644 --- a/packages/graphql-server-integration-testsuite/src/index.ts +++ b/packages/graphql-server-integration-testsuite/src/index.ts @@ -7,6 +7,7 @@ import { GraphQLSchema, GraphQLObjectType, GraphQLString, + GraphQLInt, GraphQLError, GraphQLNonNull, introspectionQuery, @@ -29,6 +30,17 @@ const queryType = new GraphQLObjectType({ return 'it works'; }, }, + testStringWithDelay: { + type: GraphQLString, + args: { + delay: { type: new GraphQLNonNull(GraphQLInt) }, + }, + resolve(root, args) { + return new Promise((resolve, reject) => { + setTimeout(() => resolve('it works'), args['delay']); + }); + }, + }, testContext: { type: GraphQLString, resolve(_, args, context) { @@ -457,6 +469,29 @@ export default (createApp: CreateAppFunc, destroyApp?: DestroyAppFunc) => { }); }); + it('can handle batch requests in parallel', function() { + // this test will fail due to timeout if running serially. + const parallels = 100; + const delayPerReq = 40; + this.timeout(3000); + + app = createApp(); + const expected = Array(parallels).fill({ + data: { testStringWithDelay: 'it works' }, + }); + const req = request(app) + .post('/graphql') + .send(Array(parallels).fill({ + query: `query test($delay: Int!) { testStringWithDelay(delay: $delay) }`, + operationName: 'test', + variables: { delay: delayPerReq }, + })); + return req.then((res) => { + expect(res.status).to.equal(200); + return expect(res.body).to.deep.equal(expected); + }); + }); + it('clones batch context', () => { app = createApp({graphqlOptions: { schema,