Skip to content

Commit

Permalink
New allowBatchedHttpRequests:false option (#5778)
Browse files Browse the repository at this point in the history
Co-authored-by: Stelios Kotanidis <sk@administrate.co>
Co-authored-by: David Glasser <glasser@davidglasser.net>
  • Loading branch information
3 people committed Nov 2, 2021
1 parent 884db19 commit 3b175dd
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ The version headers in this history reflect the versions of Apollo Server itself
-聽`apollo-server-core`: Fix build error when building with `@rollup/plugin-commonjs`. [PR #5797](https://github.com/apollographql/apollo-server/pull/5797)
- `apollo-server-plugin-response-cache`: Add missing dependency on `apollo-server-types` (broken since v3.0.0). [Issue #5804](https://github.com/apollographql/apollo-server/issues/5804) [PR #5816](https://github.com/apollographql/apollo-server/pull/5816)
- `apollo-server-core`: The default landing page plugins now take `document`, `variables`, and `headers` arguments which fill in default values if you click through to Explorer. [PR #5711](https://github.com/apollographql/apollo-server/pull/5711)
- `apollo-server-core`: Support for HTTP request batching can now be disabled by passing `allowBatchedHttpRequests: false` to `new ApolloServer`. [PR #5778](https://github.com/apollographql/apollo-server/pull/5778) [Issue #5686](https://github.com/apollographql/apollo-server/issues/5686)

## v3.4.0

Expand Down
13 changes: 13 additions & 0 deletions docs/source/api/apollo-server.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,19 @@ An object containing configuration options for connecting Apollo Server to [Apol
</td>
</tr>

<tr>
<td>

##### `allowBatchedHttpRequests`

`Boolean`
</td>
<td>

Controls whether to allow [Batching Queries](../requests/#batching) in a single HTTP Request. Defaults to `true`. If the GraphQL Server has this flag set to `false` and a request comes in formatted as an array rather than as a single request object, an error will be thrown.
</td>
</tr>


<tr>
<td colspan="2">
Expand Down
2 changes: 2 additions & 0 deletions docs/source/requests.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ You can send a batch of queries in a single POST request by providing a JSON-enc

If you send a batched request, Apollo Server responds with a corresponding array of GraphQL responses.

You can disable the processing of batched requests by passing `allowBatchedHttpRequests: false` to the `ApolloServer` constructor.

## GET requests

Apollo Server also accepts GET requests for queries (but not mutations). With a GET request, query details (`query`, `operationName`, `variables`) are provided as URL query parameters. The `variables` option is a URL-escaped JSON object.
Expand Down
91 changes: 91 additions & 0 deletions packages/apollo-server-core/src/__tests__/runHttpQuery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,95 @@ describe('runHttpQuery', () => {
});
});
});

describe('when allowBatchedHttpRequests is false', () => {
const mockDisabledBatchQueryRequest = {
method: 'GET',
query: {
query: '{ testString }',
},
options: {
debug: false,
schema,
schemaHash: 'deprecated' as SchemaHash,
allowBatchedHttpRequests: false,
},
request: new MockReq(),
};

it('succeeds when there are no batched queries in the request', async () => {
await expect(
runHttpQuery([], mockDisabledBatchQueryRequest),
).resolves.not.toThrow();
});

it('throws when there are batched queries in the request', () => {
const batchedQueryRequest = Object.assign(
{},
mockDisabledBatchQueryRequest,
{
query: [
{
query: '{ testString }',
},
{
query: '{ testString }',
},
],
},
);
return runHttpQuery([], batchedQueryRequest).catch(
(err: HttpQueryError) => {
expect(err.statusCode).toEqual(400);
expect(err.message).toEqual(
JSON.stringify({
errors: [
{
message: 'Operation batching disabled.',
extensions: { code: 'INTERNAL_SERVER_ERROR' },
},
],
}) + '\n',
);
},
);
});
});

describe('when allowBatchedHttpRequests is true', () => {
const mockEnabledBatchQueryRequest = {
method: 'GET',
query: {
query: '{ testString }',
},
options: {
debug: false,
schema,
schemaHash: 'deprecated' as SchemaHash,
allowBatchedHttpRequests: true,
},
request: new MockReq(),
};

it('succeeds when there are multiple queries in the request', async () => {
const multipleQueryRequest = Object.assign(
{},
mockEnabledBatchQueryRequest,
{
query: [
{
query: '{ testString }',
},
{
query: '{ testString }',
},
],
},
);

await expect(
runHttpQuery([], multipleQueryRequest),
).resolves.not.toThrow();
});
});
});
2 changes: 2 additions & 0 deletions packages/apollo-server-core/src/graphqlOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import type { DocumentStore } from './types';
* - (optional) fieldResolver: a custom default field resolver
* - (optional) debug: a boolean that will print additional debug logging if execution errors occur
* - (optional) parseOptions: options to pass when parsing schemas and queries
* - (optional) allowBatchedHttpRequests: a boolean to toggle whether a single request can contain an array of queries. True by default
*
*/
export interface GraphQLServerOptions<
Expand Down Expand Up @@ -63,6 +64,7 @@ export interface GraphQLServerOptions<
documentStore?: DocumentStore | null;
parseOptions?: ParseOptions;
nodeEnv?: string;
allowBatchedHttpRequests?: boolean;
}

export type DataSources<TContext> = {
Expand Down
10 changes: 10 additions & 0 deletions packages/apollo-server-core/src/runHttpQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@ export async function runHttpQuery(
debug: options.debug,

plugins: options.plugins || [],

allowBatchedHttpRequests: options.allowBatchedHttpRequests,
};

return processHTTPRequest(config, request);
Expand Down Expand Up @@ -293,6 +295,14 @@ export async function processHTTPRequest<TContext>(

try {
if (Array.isArray(requestPayload)) {
if (options.allowBatchedHttpRequests === false) {
return throwHttpGraphQLError(
400,
[new Error('Operation batching disabled.')],
options,
);
}

// We're processing a batch request
const requests = requestPayload.map((requestParams) =>
parseGraphQLRequest(httpRequest.request, requestParams),
Expand Down
1 change: 1 addition & 0 deletions packages/apollo-server-core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type BaseConfig = Pick<
| 'dataSources'
| 'cache'
| 'logger'
| 'allowBatchedHttpRequests'
>;

export type Unsubscriber = () => void;
Expand Down
37 changes: 37 additions & 0 deletions packages/apollo-server-integration-testsuite/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -878,6 +878,43 @@ export default ({
});
}, 3000); // this test will fail due to timeout if running serially.

it('disables batch requests when allowBatchedHttpRequests is false', async () => {
app = await createApp({
graphqlOptions: {
schema,
allowBatchedHttpRequests: false,
},
});

const res = await request(app)
.post('/graphql')
.send([
{
query: `
query test($echo: String){ testArgument(echo: $echo) }
query test2{ testString }`,
variables: { echo: 'world' },
operationName: 'test2',
},
{
query: `
query testX($echo: String){ testArgument(echo: $echo) }`,
variables: { echo: 'yellow' },
operationName: 'testX',
},
]);

expect(res.status).toEqual(400);
expect(res.body).toEqual({
errors: [
{
message: 'Operation batching disabled.',
extensions: { code: 'INTERNAL_SERVER_ERROR' },
},
],
});
});

it('clones batch context', async () => {
app = await createApp({
graphqlOptions: {
Expand Down

0 comments on commit 3b175dd

Please sign in to comment.