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

Commit 3a1ee00

Browse files
Allow custom handling of runtime query errors
1 parent 4b9acac commit 3a1ee00

File tree

3 files changed

+71
-10
lines changed

3 files changed

+71
-10
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ The `graphqlHTTP` function accepts the following options:
175175
- **`formatError`**: is deprecated and replaced by `customFormatErrorFn`. It will be
176176
removed in version 1.0.0.
177177

178+
- **`handleRuntimeQueryErrorFn`**: An optional function which can be used to change the headers or status code of the response in case of a runtime query error. By default, the status code is set to `500`.
179+
To ensure compatibility, make sure to take note of [the GraphQL spec regarding status codes](https://github.com/graphql/graphql-over-http/blob/master/spec/GraphQLOverHTTP.md#status-codes).
180+
178181
In addition to an object defining each option, options can also be provided as
179182
a function (or async function) which returns this options object. This function
180183
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
@@ -112,6 +112,15 @@ export interface OptionsData {
112112
*/
113113
formatError?: (error: GraphQLError) => GraphQLFormattedError;
114114

115+
/**
116+
* Use this to modify the response when a runtime query error occurs. By
117+
* default the statusCode will be set to 500.
118+
*/
119+
handleRuntimeQueryErrorFn?: (
120+
result: ExecutionResult,
121+
response: Response,
122+
) => void;
123+
115124
/**
116125
* An optional function for adding additional metadata to the GraphQL response
117126
* as a key-value object. The result will be added to "extensions" field in
@@ -197,6 +206,7 @@ export function graphqlHTTP(options: Options): Middleware {
197206
let formatErrorFn = formatError;
198207
let pretty = false;
199208
let result: ExecutionResult;
209+
let optionsData: OptionsData | undefined;
200210

201211
try {
202212
// Parse the Request to get GraphQL request parameters.
@@ -205,7 +215,7 @@ export function graphqlHTTP(options: Options): Middleware {
205215
} catch (error: unknown) {
206216
// When we failed to parse the GraphQL parameters, we still need to get
207217
// the options object, so make an options call to resolve just that.
208-
const optionsData = await resolveOptions();
218+
optionsData = await resolveOptions();
209219
pretty = optionsData.pretty ?? false;
210220
formatErrorFn =
211221
optionsData.customFormatErrorFn ??
@@ -215,7 +225,7 @@ export function graphqlHTTP(options: Options): Middleware {
215225
}
216226

217227
// Then, resolve the Options to get OptionsData.
218-
const optionsData = await resolveOptions(params);
228+
optionsData = await resolveOptions(params);
219229

220230
// Collect information from the options data object.
221231
const schema = optionsData.schema;
@@ -384,13 +394,22 @@ export function graphqlHTTP(options: Options): Middleware {
384394
}
385395
}
386396

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

396415
// Format any encountered errors.

0 commit comments

Comments
 (0)