Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .changeset/curly-friends-argue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
'@apollo/server': minor
---

Allow configuration of graphql execution options (maxCoercionErrors)

```js
const server = new ApolloServer({
typeDefs,
resolvers,
executionOptions: {
maxCoercionErrors: 50,
},
});
```
31 changes: 31 additions & 0 deletions docs/source/api/apollo-server.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,37 @@ An object that specifies how your server parses GraphQL operations. [See `graphq
<tr>
<td>

###### `executionOptions`

`Object`

</td>
<td>

An object that specifies options for GraphQL execution. This object is passed directly to the GraphQL execution engine and supports all [GraphQL-JS execution options](https://www.graphql-js.org/api-v16/execution/).

The most common use case is configuring `maxCoercionErrors` to control how many variable coercion errors are allowed before execution is terminated:

```js
const server = new ApolloServer({
typeDefs,
resolvers,
executionOptions: {
maxCoercionErrors: 10, // Default is 50
},
});
```

GraphQL execution terminates if variable coercion encounters more errors than the specified limit. This prevents your application from performing expensive operations when many input variables are malformed.

This option works with [<code>status400ForVariableCoercionErrors</code>](#status400forvariablecoercionerrors) to return appropriate HTTP status codes for variable coercion errors.

</td>
</tr>

<tr>
<td>

###### `nodeEnv`

`string`
Expand Down
2 changes: 2 additions & 0 deletions docs/source/data/errors.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

import TopLevelAwait from "../shared/top-level-await.mdx"

Use the [<code>executionOptions</code> option](../api/apollo-server#executionoptions) to configure how many variable coercion errors are allowed before execution terminates.

Check notice on line 8 in docs/source/data/errors.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/data/errors.mdx#L8

The word "option" is redundant in the link text because it already appears in the sentence. Using just the property name as the link text is cleaner and improves readability. ```suggestion Use the [<code>executionOptions</code>](../api/apollo-server#executionoptions) option to configure how many variable coercion errors are allowed before execution terminates. ```

<!-- cSpell:ignore typenam -->

Whenever Apollo Server encounters errors while processing a GraphQL operation, its response to the client includes an `errors` array containing each error that occurred. Each error in the array has an `extensions` field that provides additional useful information, including an error `code` and (while in development mode) a `stacktrace`.
Expand Down
3 changes: 3 additions & 0 deletions packages/server/src/ApolloServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
print,
printSchema,
type DocumentNode,
type ExecutionArgs,
type FormattedExecutionResult,
type GraphQLFieldResolver,
type GraphQLFormattedError,
Expand Down Expand Up @@ -152,6 +153,7 @@ export interface ApolloServerInternals<TContext extends BaseContext> {
apolloConfig: ApolloConfig;
plugins: ApolloServerPlugin<TContext>[];
parseOptions: ParseOptions;
executionOptions?: ExecutionArgs['options'];
// `undefined` means we figure out what to do during _start (because
// the default depends on whether or not we used the background version
// of start).
Expand Down Expand Up @@ -333,6 +335,7 @@ export class ApolloServer<in out TContext extends BaseContext = BaseContext> {
// `start()` will call `addDefaultPlugins` to add default plugins.
plugins: config.plugins ?? [],
parseOptions: config.parseOptions ?? {},
executionOptions: config.executionOptions ?? {},
state,
stopOnTerminationSignals: config.stopOnTerminationSignals,

Expand Down
28 changes: 28 additions & 0 deletions packages/server/src/__tests__/ApolloServer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,34 @@ describe('ApolloServer executeOperation', () => {
await server.stop();
});

it('variable coercion errors, configure max number of errors', async () => {
const server = new ApolloServer({
typeDefs,
resolvers,
status400ForVariableCoercionErrors: true,
executionOptions: {
maxCoercionErrors: 1,
},
});
await server.start();
const { body, http } = await server.executeOperation({
query: `#graphql
query NeedsArg($arg: CompoundInput!) { needsCompoundArg(aCompound: $arg) }
`,
variables: {
arg: { compound: { error1: '1', error2: '2', error3: '3' } },
},
});
const result = singleResult(body);
expect(result.errors).toHaveLength(2);
expect(result.errors?.[0].extensions?.code).toBe('BAD_USER_INPUT');
expect(result.errors?.[1].extensions?.code).toBe('INTERNAL_SERVER_ERROR');
expect(result.errors?.[1].message).toMatch(
'Too many errors processing variables, error limit reached. Execution aborted.',
);
expect(http.status).toBe(400);
});

it('passes its second argument as context object', async () => {
const server = new ApolloServer({
typeDefs,
Expand Down
3 changes: 3 additions & 0 deletions packages/server/src/externalTypes/constructor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { Logger } from '@apollo/utils.logger';
import type { IExecutableSchemaDefinition } from '@graphql-tools/schema';
import type {
DocumentNode,
ExecutionArgs,
FormattedExecutionResult,
GraphQLFieldResolver,
GraphQLFormattedError,
Expand Down Expand Up @@ -111,6 +112,8 @@ interface ApolloServerOptionsBase<TContext extends BaseContext> {
// parsing the schema.
parseOptions?: ParseOptions;

executionOptions?: ExecutionArgs['options'];

// TODO(AS6): remove this option. Configuration to `true` is default behavior
// and configuration to `false` is deprecated. If you depend on `false`
// behavior, we recommend migrating away from that at your earliest
Expand Down
1 change: 1 addition & 0 deletions packages/server/src/requestPipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,7 @@ export async function processGraphQLRequest<TContext extends BaseContext>(
operationName: request.operationName,
fieldResolver: internals.fieldResolver,
useLegacyIncremental,
options: internals.executionOptions,
});
if ('initialResult' in resultOrResults) {
return {
Expand Down