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

Commit bb23f60

Browse files
Wrap HttpError into GraphQLError (#716)
Fixes #699
1 parent b462653 commit bb23f60

File tree

4 files changed

+63
-13
lines changed

4 files changed

+63
-13
lines changed

.eslintrc.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ overrides:
474474
'@typescript-eslint/no-extraneous-class': off # TODO consider
475475
'@typescript-eslint/no-floating-promises': error
476476
'@typescript-eslint/no-for-in-array': error
477-
'@typescript-eslint/no-implicit-any-catch': off # TODO: Enable
477+
'@typescript-eslint/no-implicit-any-catch': error
478478
'@typescript-eslint/no-implied-eval': error
479479
'@typescript-eslint/no-inferrable-types':
480480
[error, { ignoreParameters: true, ignoreProperties: true }]

src/__tests__/http-test.ts

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,15 +1199,18 @@ function runTests(server: Server) {
11991199
});
12001200
});
12011201

1202-
it('allows for custom error formatting to sanitize', async () => {
1202+
it('allows for custom error formatting to sanitize GraphQL errors', async () => {
12031203
const app = server();
12041204

12051205
app.get(
12061206
urlString(),
12071207
graphqlHTTP({
12081208
schema: TestSchema,
12091209
customFormatErrorFn(error) {
1210-
return { message: 'Custom error format: ' + error.message };
1210+
return {
1211+
message:
1212+
`Custom ${error.constructor.name} format: ` + error.message,
1213+
};
12111214
},
12121215
}),
12131216
);
@@ -1223,7 +1226,35 @@ function runTests(server: Server) {
12231226
data: { thrower: null },
12241227
errors: [
12251228
{
1226-
message: 'Custom error format: Throws!',
1229+
message: 'Custom GraphQLError format: Throws!',
1230+
},
1231+
],
1232+
});
1233+
});
1234+
1235+
it('allows for custom error formatting to sanitize HTTP errors', async () => {
1236+
const app = server();
1237+
1238+
app.get(
1239+
urlString(),
1240+
graphqlHTTP({
1241+
schema: TestSchema,
1242+
customFormatErrorFn(error) {
1243+
return {
1244+
message:
1245+
`Custom ${error.constructor.name} format: ` + error.message,
1246+
};
1247+
},
1248+
}),
1249+
);
1250+
1251+
const response = await app.request().get(urlString());
1252+
1253+
expect(response.status).to.equal(400);
1254+
expect(JSON.parse(response.text)).to.deep.equal({
1255+
errors: [
1256+
{
1257+
message: 'Custom GraphQLError format: Must provide query string.',
12271258
},
12281259
],
12291260
});

src/index.ts

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,17 @@ import type {
88
ExecutionArgs,
99
ExecutionResult,
1010
FormattedExecutionResult,
11-
GraphQLError,
1211
GraphQLSchema,
1312
GraphQLFieldResolver,
1413
GraphQLTypeResolver,
1514
GraphQLFormattedError,
1615
} from 'graphql';
1716
import accepts from 'accepts';
1817
import httpError from 'http-errors';
18+
import type { HttpError } from 'http-errors';
1919
import {
2020
Source,
21+
GraphQLError,
2122
parse,
2223
validate,
2324
execute,
@@ -206,7 +207,7 @@ export function graphqlHTTP(options: Options): Middleware {
206207
// Parse the Request to get GraphQL request parameters.
207208
try {
208209
params = await getGraphQLParams(request);
209-
} catch (error) {
210+
} catch (error: unknown) {
210211
// When we failed to parse the GraphQL parameters, we still need to get
211212
// the options object, so make an options call to resolve just that.
212213
const optionsData = await resolveOptions();
@@ -284,7 +285,7 @@ export function graphqlHTTP(options: Options): Middleware {
284285
let documentAST;
285286
try {
286287
documentAST = parseFn(new Source(query, 'GraphQL request'));
287-
} catch (syntaxError) {
288+
} catch (syntaxError: unknown) {
288289
// Return 400: Bad Request if any syntax errors errors exist.
289290
throw httpError(400, 'GraphQL syntax error.', {
290291
graphqlErrors: [syntaxError],
@@ -337,7 +338,7 @@ export function graphqlHTTP(options: Options): Middleware {
337338
fieldResolver,
338339
typeResolver,
339340
});
340-
} catch (contextError) {
341+
} catch (contextError: unknown) {
341342
// Return 400: Bad Request if any execution context errors exist.
342343
throw httpError(400, 'GraphQL execution context error.', {
343344
graphqlErrors: [contextError],
@@ -359,9 +360,15 @@ export function graphqlHTTP(options: Options): Middleware {
359360
result = { ...result, extensions };
360361
}
361362
}
362-
} catch (error) {
363+
} catch (rawError: unknown) {
363364
// If an error was caught, report the httpError status, or 500.
364-
response.statusCode = error.status ?? 500;
365+
const error: HttpError = httpError(
366+
500,
367+
/* istanbul ignore next: Thrown by underlying library. */
368+
rawError instanceof Error ? rawError : String(rawError),
369+
);
370+
371+
response.statusCode = error.status;
365372

366373
const { headers } = error;
367374
if (headers != null) {
@@ -370,7 +377,19 @@ export function graphqlHTTP(options: Options): Middleware {
370377
}
371378
}
372379

373-
result = { data: undefined, errors: error.graphqlErrors ?? [error] };
380+
if (error.graphqlErrors == null) {
381+
const graphqlError = new GraphQLError(
382+
error.message,
383+
undefined,
384+
undefined,
385+
undefined,
386+
undefined,
387+
error,
388+
);
389+
result = { data: undefined, errors: [graphqlError] };
390+
} else {
391+
result = { data: undefined, errors: error.graphqlErrors };
392+
}
374393
}
375394

376395
// If no data was included in the result, that indicates a runtime query
@@ -482,7 +501,7 @@ export async function getGraphQLParams(
482501
if (typeof variables === 'string') {
483502
try {
484503
variables = JSON.parse(variables);
485-
} catch (error) {
504+
} catch {
486505
throw httpError(400, 'Variables are invalid JSON.');
487506
}
488507
} else if (typeof variables !== 'object') {

src/parseBody.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export async function parseBody(
5151
if (jsonObjRegex.test(rawBody)) {
5252
try {
5353
return JSON.parse(rawBody);
54-
} catch (error) {
54+
} catch {
5555
// Do nothing
5656
}
5757
}

0 commit comments

Comments
 (0)