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

Commit ec7d977

Browse files
Allow custom handling of runtime query errors
1 parent 0fe6510 commit ec7d977

File tree

3 files changed

+72
-10
lines changed

3 files changed

+72
-10
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@ The `graphqlHTTP` function accepts the following options:
123123
- **`formatError`**: is deprecated and replaced by `customFormatErrorFn`. It will be
124124
removed in version 1.0.0.
125125

126+
- **`handleRuntimeQueryErrorFn`**: An optional function which can be used to change the status code
127+
or headers of the response in case of a runtime query error. By default, the status code is set to
128+
`500`.
129+
126130
In addition to an object defining each option, options can also be provided as
127131
a function (or async function) which returns this options object. This function
128132
is provided the arguments `(request, response, graphQLParams)` and is called

src/__tests__/usage-test.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ import express from 'express';
22
import request from 'supertest';
33
import { expect } from 'chai';
44
import { describe, it } from 'mocha';
5-
import { GraphQLSchema } from 'graphql';
5+
import {
6+
GraphQLNonNull,
7+
GraphQLObjectType,
8+
GraphQLSchema,
9+
GraphQLString,
10+
} from 'graphql';
611

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

@@ -96,6 +101,40 @@ describe('Useful errors when incorrectly used', () => {
96101
});
97102
});
98103

104+
it('uses the custom runtime query error handling function', async () => {
105+
const schema = new GraphQLSchema({
106+
query: new GraphQLObjectType({
107+
name: 'QueryRoot',
108+
fields: {
109+
test: {
110+
type: new GraphQLNonNull(GraphQLString),
111+
resolve() {
112+
throw new Error('Throws!');
113+
},
114+
},
115+
},
116+
}),
117+
});
118+
119+
const app = express();
120+
121+
app.use(
122+
'/graphql',
123+
graphqlHTTP({
124+
handleRuntimeQueryErrorFn(_, response) {
125+
response.setHeader('customRuntimeQueryError', "I'm a teapot");
126+
response.statusCode = 418;
127+
},
128+
schema,
129+
}),
130+
);
131+
132+
const response = await request(app).get('/graphql?query={test}');
133+
134+
expect(response.status).to.equal(418);
135+
expect(response.get('customRuntimeQueryError')).to.equal("I'm a teapot");
136+
});
137+
99138
it('validates schema before executing request', async () => {
100139
// @ts-expect-error
101140
const schema = new GraphQLSchema({ directives: [null] });

src/index.ts

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,15 @@ export interface OptionsData {
114114
*/
115115
formatError?: (error: GraphQLError) => GraphQLFormattedError;
116116

117+
/**
118+
* Use this to modify the response when a runtime query error occurs. By
119+
* default the statusCode will be set to 500.
120+
*/
121+
handleRuntimeQueryErrorFn?: (
122+
result: ExecutionResult,
123+
response: Response,
124+
) => void;
125+
117126
/**
118127
* An optional function for adding additional metadata to the GraphQL response
119128
* as a key-value object. The result will be added to "extensions" field in
@@ -201,6 +210,7 @@ export function graphqlHTTP(options: Options): Middleware {
201210
let formatErrorFn = formatError;
202211
let pretty = false;
203212
let result: ExecutionResult;
213+
let optionsData: OptionsData | undefined;
204214

205215
try {
206216
// Parse the Request to get GraphQL request parameters.
@@ -209,7 +219,7 @@ export function graphqlHTTP(options: Options): Middleware {
209219
} catch (error) {
210220
// When we failed to parse the GraphQL parameters, we still need to get
211221
// the options object, so make an options call to resolve just that.
212-
const optionsData = await resolveOptions();
222+
optionsData = await resolveOptions();
213223
pretty = optionsData.pretty ?? false;
214224
formatErrorFn =
215225
optionsData.customFormatErrorFn ??
@@ -219,7 +229,7 @@ export function graphqlHTTP(options: Options): Middleware {
219229
}
220230

221231
// Then, resolve the Options to get OptionsData.
222-
const optionsData: OptionsData = await resolveOptions(params);
232+
optionsData = await resolveOptions(params);
223233

224234
// Collect information from the options data object.
225235
const schema = optionsData.schema;
@@ -373,13 +383,22 @@ export function graphqlHTTP(options: Options): Middleware {
373383
result = { data: undefined, errors: error.graphqlErrors ?? [error] };
374384
}
375385

376-
// If no data was included in the result, that indicates a runtime query
377-
// error, indicate as such with a generic status code.
378-
// Note: Information about the error itself will still be contained in
379-
// the resulting JSON payload.
380-
// https://graphql.github.io/graphql-spec/#sec-Data
381-
if (response.statusCode === 200 && result.data == null) {
382-
response.statusCode = 500;
386+
if (result.errors != null || result.data == null) {
387+
const handleRuntimeQueryErrorFn =
388+
optionsData?.handleRuntimeQueryErrorFn ??
389+
((_result, _response) => {
390+
// If no data was included in the result, that indicates a runtime query
391+
// error, indicate as such with a generic status code.
392+
// Note: Information about the error itself will still be contained in
393+
// the resulting JSON payload.
394+
// https://graphql.github.io/graphql-spec/#sec-Data
395+
396+
if (_response.statusCode === 200 && _result.data == null) {
397+
_response.statusCode = 500;
398+
}
399+
});
400+
401+
handleRuntimeQueryErrorFn(result, response);
383402
}
384403

385404
// Format any encountered errors.

0 commit comments

Comments
 (0)