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

Commit 43ba606

Browse files
MatthiasKunnenacao
authored andcommitted
Allow custom handling of runtime query errors
1 parent 6f88898 commit 43ba606

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
@@ -173,6 +173,10 @@ The `graphqlHTTP` function accepts the following options:
173173
- **`formatError`**: is deprecated and replaced by `customFormatErrorFn`. It will be
174174
removed in version 1.0.0.
175175

176+
- **`handleRuntimeQueryErrorFn`**: An optional function which can be used to change the status code
177+
or headers of the response in case of a runtime query error. By default, the status code is set to
178+
`500`.
179+
176180
In addition to an object defining each option, options can also be provided as
177181
a function (or async function) which returns this options object. This function
178182
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
@@ -115,6 +115,15 @@ export interface OptionsData {
115115
*/
116116
formatError?: (error: GraphQLError) => GraphQLFormattedError;
117117

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

204214
try {
205215
// Parse the Request to get GraphQL request parameters.
@@ -208,7 +218,7 @@ export function graphqlHTTP(options: Options): Middleware {
208218
} catch (error: unknown) {
209219
// When we failed to parse the GraphQL parameters, we still need to get
210220
// the options object, so make an options call to resolve just that.
211-
const optionsData = await resolveOptions();
221+
optionsData = await resolveOptions();
212222
pretty = optionsData.pretty ?? false;
213223
formatErrorFn =
214224
optionsData.customFormatErrorFn ??
@@ -218,7 +228,7 @@ export function graphqlHTTP(options: Options): Middleware {
218228
}
219229

220230
// Then, resolve the Options to get OptionsData.
221-
const optionsData: OptionsData = await resolveOptions(params);
231+
optionsData = await resolveOptions(params);
222232

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

391-
// If no data was included in the result, that indicates a runtime query
392-
// error, indicate as such with a generic status code.
393-
// Note: Information about the error itself will still be contained in
394-
// the resulting JSON payload.
395-
// https://graphql.github.io/graphql-spec/#sec-Data
396-
if (response.statusCode === 200 && result.data == null) {
397-
response.statusCode = 500;
401+
if (result.errors != null || result.data == null) {
402+
const handleRuntimeQueryErrorFn =
403+
optionsData?.handleRuntimeQueryErrorFn ??
404+
((_result, _response) => {
405+
// If no data was included in the result, that indicates a runtime query
406+
// error, indicate as such with a generic status code.
407+
// Note: Information about the error itself will still be contained in
408+
// the resulting JSON payload.
409+
// https://graphql.github.io/graphql-spec/#sec-Data
410+
411+
if (_response.statusCode === 200 && _result.data == null) {
412+
_response.statusCode = 500;
413+
}
414+
});
415+
416+
handleRuntimeQueryErrorFn(result, response);
398417
}
399418

400419
// Format any encountered errors.

0 commit comments

Comments
 (0)