Skip to content

Commit 4ca6065

Browse files
committed
feat: add validationMaxErrors option
1 parent 9524f3f commit 4ca6065

File tree

5 files changed

+55
-1
lines changed

5 files changed

+55
-1
lines changed

packages/server/src/ApolloServer.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ export interface ApolloServerInternals<TContext extends BaseContext> {
156156
state: ServerState;
157157
gatewayExecutor: GatewayExecutor | null;
158158
dangerouslyDisableValidation?: boolean;
159+
validationMaxErrors?: number;
159160
formatError?: (
160161
formattedError: GraphQLFormattedError,
161162
error: unknown,
@@ -304,6 +305,7 @@ export class ApolloServer<in out TContext extends BaseContext = BaseContext> {
304305
hideSchemaDetailsFromClientErrors,
305306
dangerouslyDisableValidation:
306307
config.dangerouslyDisableValidation ?? false,
308+
validationMaxErrors: config.validationMaxErrors,
307309
fieldResolver: config.fieldResolver,
308310
includeStacktraceInErrorResponses:
309311
config.includeStacktraceInErrorResponses ??

packages/server/src/__tests__/ApolloServer.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,9 @@ describe('ApolloServer start', () => {
425425
});
426426
});
427427

428-
function singleResult(body: GraphQLResponseBody): FormattedExecutionResult {
428+
export function singleResult(
429+
body: GraphQLResponseBody,
430+
): FormattedExecutionResult {
429431
if (body.kind === 'single') {
430432
return body.singleResult;
431433
}

packages/server/src/__tests__/runQuery.test.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
} from '..';
2626
import { mockLogger } from './mockLogger';
2727
import { jest, describe, it, expect } from '@jest/globals';
28+
import { singleResult } from './ApolloServer.test';
2829

2930
async function runQuery(
3031
config: ApolloServerOptions<BaseContext>,
@@ -1192,4 +1193,51 @@ describe('parsing and validation cache', () => {
11921193
expect(parsingDidStart.mock.calls.length).toBe(6);
11931194
expect(validationDidStart.mock.calls.length).toBe(6);
11941195
});
1196+
1197+
describe('validationMaxErrors option', () => {
1198+
it('should be 100 by default', async () => {
1199+
const server = new ApolloServer({
1200+
schema,
1201+
});
1202+
await server.start();
1203+
1204+
const vars = new Array(1000).fill(`$a:a`).join(',');
1205+
const query = `query aaa (${vars}) { a }`;
1206+
1207+
const res = await server.executeOperation({ query });
1208+
expect(res.http.status).toBe(400);
1209+
1210+
const body = singleResult(res.body);
1211+
1212+
// 100 by default plus one "Too many validation errors" error
1213+
// https://github.com/graphql/graphql-js/blob/main/src/validation/validate.ts#L46
1214+
expect(body.errors).toHaveLength(101);
1215+
await server.stop();
1216+
});
1217+
it('aborts the validation if max errors more than expected', async () => {
1218+
const server = new ApolloServer({
1219+
schema,
1220+
validationMaxErrors: 1,
1221+
});
1222+
await server.start();
1223+
1224+
const vars = new Array(1000).fill(`$a:a`).join(',');
1225+
const query = `query aaa (${vars}) { a }`;
1226+
1227+
const res = await server.executeOperation({ query });
1228+
expect(res.http.status).toBe(400);
1229+
1230+
const body = singleResult(res.body);
1231+
1232+
expect(body.errors).toHaveLength(2);
1233+
expect(body.errors?.[0]).toMatchObject({
1234+
message: `There can be only one variable named "$a".`,
1235+
});
1236+
expect(body.errors?.[1]).toMatchObject({
1237+
message: `Too many validation errors, error limit reached. Validation aborted.`,
1238+
});
1239+
1240+
await server.stop();
1241+
});
1242+
});
11951243
});

packages/server/src/externalTypes/constructor.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ interface ApolloServerOptionsBase<TContext extends BaseContext> {
101101
nodeEnv?: string;
102102
documentStore?: DocumentStore | null;
103103
dangerouslyDisableValidation?: boolean;
104+
validationMaxErrors?: number;
104105
csrfPrevention?: CSRFPreventionOptions | boolean;
105106

106107
// Used for parsing operations; unlike in AS3, this is not also used for

packages/server/src/requestPipeline.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ export async function processGraphQLRequest<TContext extends BaseContext>(
247247
schemaDerivedData.schema,
248248
requestContext.document,
249249
[...specifiedRules, ...internals.validationRules],
250+
{ maxErrors: internals.validationMaxErrors },
250251
);
251252

252253
if (validationErrors.length === 0) {

0 commit comments

Comments
 (0)