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

Commit

Permalink
Allow custom handling of runtime query errors
Browse files Browse the repository at this point in the history
  • Loading branch information
MatthiasKunnen authored and acao committed Dec 8, 2020
1 parent 6f88898 commit 43ba606
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 10 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,10 @@ The `graphqlHTTP` function accepts the following options:
- **`formatError`**: is deprecated and replaced by `customFormatErrorFn`. It will be
removed in version 1.0.0.

- **`handleRuntimeQueryErrorFn`**: An optional function which can be used to change the status code
or headers of the response in case of a runtime query error. By default, the status code is set to
`500`.

In addition to an object defining each option, options can also be provided as
a function (or async function) which returns this options object. This function
is provided the arguments `(request, response, graphQLParams)` and is called
Expand Down
41 changes: 40 additions & 1 deletion src/__tests__/usage-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import express from 'express';
import request from 'supertest';
import { expect } from 'chai';
import { describe, it } from 'mocha';
import { GraphQLSchema } from 'graphql';
import {
GraphQLNonNull,
GraphQLObjectType,
GraphQLSchema,
GraphQLString,
} from 'graphql';

import { graphqlHTTP } from '../index';

Expand Down Expand Up @@ -96,6 +101,40 @@ describe('Useful errors when incorrectly used', () => {
});
});

it('uses the custom runtime query error handling function', async () => {
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'QueryRoot',
fields: {
test: {
type: new GraphQLNonNull(GraphQLString),
resolve() {
throw new Error('Throws!');
},
},
},
}),
});

const app = express();

app.use(
'/graphql',
graphqlHTTP({
handleRuntimeQueryErrorFn(_, response) {
response.setHeader('customRuntimeQueryError', "I'm a teapot");
response.statusCode = 418;
},
schema,
}),
);

const response = await request(app).get('/graphql?query={test}');

expect(response.status).to.equal(418);
expect(response.get('customRuntimeQueryError')).to.equal("I'm a teapot");
});

it('validates schema before executing request', async () => {
// @ts-expect-error
const schema = new GraphQLSchema({ directives: [null] });
Expand Down
37 changes: 28 additions & 9 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,15 @@ export interface OptionsData {
*/
formatError?: (error: GraphQLError) => GraphQLFormattedError;

/**
* Use this to modify the response when a runtime query error occurs. By
* default the statusCode will be set to 500.
*/
handleRuntimeQueryErrorFn?: (
result: ExecutionResult,
response: Response,
) => void;

/**
* An optional function for adding additional metadata to the GraphQL response
* as a key-value object. The result will be added to "extensions" field in
Expand Down Expand Up @@ -200,6 +209,7 @@ export function graphqlHTTP(options: Options): Middleware {
let formatErrorFn = formatError;
let pretty = false;
let result: ExecutionResult;
let optionsData: OptionsData | undefined;

try {
// Parse the Request to get GraphQL request parameters.
Expand All @@ -208,7 +218,7 @@ export function graphqlHTTP(options: Options): Middleware {
} 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();
optionsData = await resolveOptions();
pretty = optionsData.pretty ?? false;
formatErrorFn =
optionsData.customFormatErrorFn ??
Expand All @@ -218,7 +228,7 @@ export function graphqlHTTP(options: Options): Middleware {
}

// Then, resolve the Options to get OptionsData.
const optionsData: OptionsData = await resolveOptions(params);
optionsData = await resolveOptions(params);

// Collect information from the options data object.
const schema = optionsData.schema;
Expand Down Expand Up @@ -388,13 +398,22 @@ export function graphqlHTTP(options: Options): Middleware {
}
}

// If no data was included in the result, that indicates a runtime query
// error, indicate as such with a generic status code.
// Note: Information about the error itself will still be contained in
// the resulting JSON payload.
// https://graphql.github.io/graphql-spec/#sec-Data
if (response.statusCode === 200 && result.data == null) {
response.statusCode = 500;
if (result.errors != null || result.data == null) {
const handleRuntimeQueryErrorFn =
optionsData?.handleRuntimeQueryErrorFn ??
((_result, _response) => {
// If no data was included in the result, that indicates a runtime query
// error, indicate as such with a generic status code.
// Note: Information about the error itself will still be contained in
// the resulting JSON payload.
// https://graphql.github.io/graphql-spec/#sec-Data

if (_response.statusCode === 200 && _result.data == null) {
_response.statusCode = 500;
}
});

handleRuntimeQueryErrorFn(result, response);
}

// Format any encountered errors.
Expand Down

0 comments on commit 43ba606

Please sign in to comment.