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

Wrap HttpError into GraphQLError #716

Merged
merged 1 commit into from
Nov 9, 2020
Merged
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
2 changes: 1 addition & 1 deletion .eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ overrides:
'@typescript-eslint/no-extraneous-class': off # TODO consider
'@typescript-eslint/no-floating-promises': error
'@typescript-eslint/no-for-in-array': error
'@typescript-eslint/no-implicit-any-catch': off # TODO: Enable
'@typescript-eslint/no-implicit-any-catch': error
'@typescript-eslint/no-implied-eval': error
'@typescript-eslint/no-inferrable-types':
[error, { ignoreParameters: true, ignoreProperties: true }]
Expand Down
37 changes: 34 additions & 3 deletions src/__tests__/http-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1199,15 +1199,18 @@ function runTests(server: Server) {
});
});

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

app.get(
urlString(),
graphqlHTTP({
schema: TestSchema,
customFormatErrorFn(error) {
return { message: 'Custom error format: ' + error.message };
return {
message:
`Custom ${error.constructor.name} format: ` + error.message,
};
},
}),
);
Expand All @@ -1223,7 +1226,35 @@ function runTests(server: Server) {
data: { thrower: null },
errors: [
{
message: 'Custom error format: Throws!',
message: 'Custom GraphQLError format: Throws!',
},
],
});
});

it('allows for custom error formatting to sanitize HTTP errors', async () => {
const app = server();

app.get(
urlString(),
graphqlHTTP({
schema: TestSchema,
customFormatErrorFn(error) {
return {
message:
`Custom ${error.constructor.name} format: ` + error.message,
};
},
}),
);

const response = await app.request().get(urlString());

expect(response.status).to.equal(400);
expect(JSON.parse(response.text)).to.deep.equal({
errors: [
{
message: 'Custom GraphQLError format: Must provide query string.',
},
],
});
Expand Down
35 changes: 27 additions & 8 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@ import type {
ExecutionArgs,
ExecutionResult,
FormattedExecutionResult,
GraphQLError,
GraphQLSchema,
GraphQLFieldResolver,
GraphQLTypeResolver,
GraphQLFormattedError,
} from 'graphql';
import accepts from 'accepts';
import httpError from 'http-errors';
import type { HttpError } from 'http-errors';
import {
Source,
GraphQLError,
parse,
validate,
execute,
Expand Down Expand Up @@ -206,7 +207,7 @@ export function graphqlHTTP(options: Options): Middleware {
// Parse the Request to get GraphQL request parameters.
try {
params = await getGraphQLParams(request);
} catch (error) {
} catch (error: unknown) {
// When we failed to parse the GraphQL parameters, we still need to get
// the options object, so make an options call to resolve just that.
const optionsData = await resolveOptions();
Expand Down Expand Up @@ -284,7 +285,7 @@ export function graphqlHTTP(options: Options): Middleware {
let documentAST;
try {
documentAST = parseFn(new Source(query, 'GraphQL request'));
} catch (syntaxError) {
} catch (syntaxError: unknown) {
// Return 400: Bad Request if any syntax errors errors exist.
throw httpError(400, 'GraphQL syntax error.', {
graphqlErrors: [syntaxError],
Expand Down Expand Up @@ -337,7 +338,7 @@ export function graphqlHTTP(options: Options): Middleware {
fieldResolver,
typeResolver,
});
} catch (contextError) {
} catch (contextError: unknown) {
// Return 400: Bad Request if any execution context errors exist.
throw httpError(400, 'GraphQL execution context error.', {
graphqlErrors: [contextError],
Expand All @@ -359,9 +360,15 @@ export function graphqlHTTP(options: Options): Middleware {
result = { ...result, extensions };
}
}
} catch (error) {
} catch (rawError: unknown) {
// If an error was caught, report the httpError status, or 500.
response.statusCode = error.status ?? 500;
const error: HttpError = httpError(
500,
/* istanbul ignore next: Thrown by underlying library. */
rawError instanceof Error ? rawError : String(rawError),
);

response.statusCode = error.status;

const { headers } = error;
if (headers != null) {
Expand All @@ -370,7 +377,19 @@ export function graphqlHTTP(options: Options): Middleware {
}
}

result = { data: undefined, errors: error.graphqlErrors ?? [error] };
if (error.graphqlErrors == null) {
const graphqlError = new GraphQLError(
error.message,
undefined,
undefined,
undefined,
undefined,
error,
);
result = { data: undefined, errors: [graphqlError] };
} else {
result = { data: undefined, errors: error.graphqlErrors };
}
}

// If no data was included in the result, that indicates a runtime query
Expand Down Expand Up @@ -482,7 +501,7 @@ export async function getGraphQLParams(
if (typeof variables === 'string') {
try {
variables = JSON.parse(variables);
} catch (error) {
} catch {
throw httpError(400, 'Variables are invalid JSON.');
}
} else if (typeof variables !== 'object') {
Expand Down
2 changes: 1 addition & 1 deletion src/parseBody.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export async function parseBody(
if (jsonObjRegex.test(rawBody)) {
try {
return JSON.parse(rawBody);
} catch (error) {
} catch {
// Do nothing
}
}
Expand Down