graphql-http 1.17.0 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -54,9 +54,13 @@ export function serverAudits(opts) {
54
54
  ressert(res).header('content-type').toContain('application/json');
55
55
  }),
56
56
  audit('80D8', 'SHOULD assume application/json content-type when accept is missing', async () => {
57
- const url = new URL(await getUrl(opts.url));
58
- url.searchParams.set('query', '{ __typename }');
59
- const res = await fetchFn(url.toString());
57
+ const res = await fetchFn(await getUrl(opts.url), {
58
+ method: 'POST',
59
+ headers: {
60
+ 'content-type': 'application/json',
61
+ },
62
+ body: JSON.stringify({ query: '{ __typename }' }),
63
+ });
60
64
  ressert(res).status.toBe(200);
61
65
  ressert(res).header('content-type').toContain('application/json');
62
66
  }),
@@ -83,7 +87,9 @@ export function serverAudits(opts) {
83
87
  headers: {
84
88
  'content-type': 'application/json; charset=utf-8',
85
89
  },
86
- body: JSON.stringify({ query: '{ __typename }' }),
90
+ body: JSON.stringify({
91
+ query: '{ __type(name: "Run🏃Swim🏊") { name } }',
92
+ }),
87
93
  });
88
94
  ressert(res).status.toBe(200);
89
95
  }),
@@ -125,7 +131,7 @@ export function serverAudits(opts) {
125
131
  ressert(res).status.toBeBetween(400, 499);
126
132
  }),
127
133
  // Request POST
128
- audit('9ABE', 'SHOULD respond with 4xx status code if content-type is not supplied on POST requests', async () => {
134
+ audit('9ABE', 'MAY respond with 4xx status code if content-type is not supplied on POST requests', async () => {
129
135
  const res = await fetchFn(await getUrl(opts.url), {
130
136
  method: 'POST',
131
137
  });
@@ -139,23 +145,15 @@ export function serverAudits(opts) {
139
145
  });
140
146
  ressert(res).status.toBe(200);
141
147
  }),
142
- audit('7267', 'MUST require a request body on POST', async () => {
143
- var _a;
148
+ audit('A5BF', 'MAY use 400 status code when request body is missing on POST', async () => {
144
149
  const res = await fetchFn(await getUrl(opts.url), {
145
150
  method: 'POST',
146
151
  headers: { 'content-type': 'application/json' },
147
152
  });
148
- if ((_a = res.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.includes('application/json')) {
149
- await ressert(res).bodyAsExecutionResult.toHaveProperty('errors');
150
- }
151
- else {
152
- ressert(res).status.toBe(400);
153
- }
153
+ ressert(res).status.toBe(400);
154
154
  }),
155
155
  // Request Parameters
156
- audit(
157
- // TODO: convert to MUST after watershed
158
- '6610', 'SHOULD use 400 status code on missing {query} parameter when accepting application/graphql-response+json', async () => {
156
+ audit('423L', 'MAY use 400 status code on missing {query} parameter', async () => {
159
157
  const res = await fetchFn(await getUrl(opts.url), {
160
158
  method: 'POST',
161
159
  headers: {
@@ -166,26 +164,11 @@ export function serverAudits(opts) {
166
164
  });
167
165
  ressert(res).status.toBe(400);
168
166
  }),
169
- audit('3715', 'SHOULD use 200 status code with errors field on missing {query} parameter when accepting application/json', async () => {
167
+ ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid, index) => audit(`LKJ${index}`, `MAY use 400 status code on ${extendedTypeof(invalid)} {query} parameter`, async () => {
170
168
  const res = await fetchFn(await getUrl(opts.url), {
171
169
  method: 'POST',
172
170
  headers: {
173
171
  'content-type': 'application/json',
174
- accept: 'application/json',
175
- },
176
- body: JSON.stringify({ notquery: '{ __typename }' }),
177
- });
178
- ressert(res).status.toBe(200);
179
- await ressert(res).bodyAsExecutionResult.toHaveProperty('errors');
180
- }),
181
- ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid, index) => audit(`4F5${index}`,
182
- // TODO: convert to MUST after watershed
183
- `SHOULD use 400 status code on ${extendedTypeof(invalid)} {query} parameter when accepting application/graphql-response+json`, async () => {
184
- const res = await fetchFn(await getUrl(opts.url), {
185
- method: 'POST',
186
- headers: {
187
- 'content-type': 'application/json',
188
- accept: 'application/graphql-response+json',
189
172
  },
190
173
  body: JSON.stringify({
191
174
  query: invalid,
@@ -193,20 +176,6 @@ export function serverAudits(opts) {
193
176
  });
194
177
  ressert(res).status.toBe(400);
195
178
  })),
196
- ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid, index) => audit(`9FE${index}`, `SHOULD use 200 status code with errors field on ${extendedTypeof(invalid)} {query} parameter when accepting application/json`, async () => {
197
- const res = await fetchFn(await getUrl(opts.url), {
198
- method: 'POST',
199
- headers: {
200
- 'content-type': 'application/json',
201
- accept: 'application/json',
202
- },
203
- body: JSON.stringify({
204
- query: invalid,
205
- }),
206
- });
207
- ressert(res).status.toBe(200);
208
- await ressert(res).bodyAsExecutionResult.toHaveProperty('errors');
209
- })),
210
179
  audit(
211
180
  // TODO: convert to MUST after watershed
212
181
  '34A2', 'SHOULD allow string {query} parameter when accepting application/graphql-response+json', async () => {
@@ -236,14 +205,11 @@ export function serverAudits(opts) {
236
205
  ressert(res).status.toBe(200);
237
206
  await ressert(res).bodyAsExecutionResult.notToHaveProperty('errors');
238
207
  }),
239
- ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid, index) => audit(`E3E${index}`,
240
- // TODO: convert to MUST after watershed
241
- `SHOULD use 400 status code on ${extendedTypeof(invalid)} {operationName} parameter when accepting application/graphql-response+json`, async () => {
208
+ ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid, index) => audit(`6C0${index}`, `MAY use 400 status code on ${extendedTypeof(invalid)} {operationName} parameter`, async () => {
242
209
  const res = await fetchFn(await getUrl(opts.url), {
243
210
  method: 'POST',
244
211
  headers: {
245
212
  'content-type': 'application/json',
246
- accept: 'application/graphql-response+json',
247
213
  },
248
214
  body: JSON.stringify({
249
215
  operationName: invalid,
@@ -252,21 +218,6 @@ export function serverAudits(opts) {
252
218
  });
253
219
  ressert(res).status.toBe(400);
254
220
  })),
255
- ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid, index) => audit(`FB9${index}`, `SHOULD use 200 status code with errors field on ${extendedTypeof(invalid)} {operationName} parameter when accepting application/json`, async () => {
256
- const res = await fetchFn(await getUrl(opts.url), {
257
- method: 'POST',
258
- headers: {
259
- 'content-type': 'application/json',
260
- accept: 'application/json',
261
- },
262
- body: JSON.stringify({
263
- operationName: invalid,
264
- query: '{ __typename }',
265
- }),
266
- });
267
- ressert(res).status.toBe(200);
268
- await ressert(res).bodyAsExecutionResult.toHaveProperty('errors');
269
- })),
270
221
  audit(
271
222
  // TODO: convert to MUST after watershed
272
223
  '8161', 'SHOULD allow string {operationName} parameter when accepting application/graphql-response+json', async () => {
@@ -332,14 +283,11 @@ export function serverAudits(opts) {
332
283
  await ressert(res).bodyAsExecutionResult.notToHaveProperty('errors');
333
284
  }),
334
285
  ]),
335
- ...['string', 0, false, ['array']].map((invalid, index) => audit(`69B${index}`,
336
- // TODO: convert to MUST after watershed
337
- `SHOULD use 400 status code on ${extendedTypeof(invalid)} {variables} parameter when accepting application/graphql-response+json`, async () => {
286
+ ...['string', 0, false, ['array']].map((invalid, index) => audit(`476${index}`, `MAY use 400 status code on ${extendedTypeof(invalid)} {variables} parameter`, async () => {
338
287
  const res = await fetchFn(await getUrl(opts.url), {
339
288
  method: 'POST',
340
289
  headers: {
341
290
  'content-type': 'application/json',
342
- accept: 'application/graphql-response+json',
343
291
  },
344
292
  body: JSON.stringify({
345
293
  query: '{ __typename }',
@@ -348,21 +296,6 @@ export function serverAudits(opts) {
348
296
  });
349
297
  ressert(res).status.toBe(400);
350
298
  })),
351
- ...['string', 0, false, ['array']].map((invalid, index) => audit(`F05${index}`, `SHOULD use 200 status code with errors field on ${extendedTypeof(invalid)} {variables} parameter when accepting application/json`, async () => {
352
- const res = await fetchFn(await getUrl(opts.url), {
353
- method: 'POST',
354
- headers: {
355
- 'content-type': 'application/json',
356
- accept: 'application/json',
357
- },
358
- body: JSON.stringify({
359
- query: '{ __typename }',
360
- variables: invalid,
361
- }),
362
- });
363
- ressert(res).status.toBe(200);
364
- await ressert(res).bodyAsExecutionResult.toHaveProperty('errors');
365
- })),
366
299
  audit(
367
300
  // TODO: convert to MUST after watershed
368
301
  '2EA1', 'SHOULD allow map {variables} parameter when accepting application/graphql-response+json', async () => {
@@ -419,14 +352,13 @@ export function serverAudits(opts) {
419
352
  ressert(res).status.toBe(200);
420
353
  await ressert(res).bodyAsExecutionResult.notToHaveProperty('errors');
421
354
  }),
422
- ...['string', 0, false, ['array']].map((invalid, index) => audit(`904${index}`,
355
+ ...['string', 0, false, ['array']].map((invalid, index) => audit(`58B${index}`,
423
356
  // TODO: convert to MUST after watershed
424
- `SHOULD use 400 status code on ${extendedTypeof(invalid)} {extensions} parameter when accepting application/graphql-response+json`, async () => {
357
+ `MAY use 400 status code on ${extendedTypeof(invalid)} {extensions} parameter`, async () => {
425
358
  const res = await fetchFn(await getUrl(opts.url), {
426
359
  method: 'POST',
427
360
  headers: {
428
361
  'content-type': 'application/json',
429
- accept: 'application/graphql-response+json',
430
362
  },
431
363
  body: JSON.stringify({
432
364
  query: '{ __typename }',
@@ -435,21 +367,6 @@ export function serverAudits(opts) {
435
367
  });
436
368
  ressert(res).status.toBe(400);
437
369
  })),
438
- ...['string', 0, false, ['array']].map((invalid, index) => audit(`368${index}`, `SHOULD use 200 status code with errors field on ${extendedTypeof(invalid)} {extensions} parameter when accepting application/json`, async () => {
439
- const res = await fetchFn(await getUrl(opts.url), {
440
- method: 'POST',
441
- headers: {
442
- 'content-type': 'application/json',
443
- accept: 'application/json',
444
- },
445
- body: JSON.stringify({
446
- query: '{ __typename }',
447
- extensions: invalid,
448
- }),
449
- });
450
- ressert(res).status.toBe(200);
451
- await ressert(res).bodyAsExecutionResult.toHaveProperty('errors');
452
- })),
453
370
  audit(
454
371
  // TODO: convert to MUST after watershed
455
372
  '428F', 'SHOULD allow map {extensions} parameter when accepting application/graphql-response+json', async () => {
@@ -481,122 +398,91 @@ export function serverAudits(opts) {
481
398
  ressert(res).status.toBe(200);
482
399
  await ressert(res).bodyAsExecutionResult.notToHaveProperty('errors');
483
400
  }),
484
- // TODO: audit('39AA', 'MUST accept a map for the {extensions} parameter'),
485
- // Response application/json
486
- audit('D477', 'SHOULD use 200 status code on JSON parsing failure when accepting application/json', async () => {
401
+ audit('B6DC', 'MAY use 4xx or 5xx status codes on JSON parsing failure', async () => {
487
402
  const res = await fetchFn(await getUrl(opts.url), {
488
403
  method: 'POST',
489
404
  headers: {
490
405
  'content-type': 'application/json',
491
- accept: 'application/json',
492
406
  },
493
407
  body: '{ "not a JSON',
494
408
  });
495
- ressert(res).status.toBe(200);
496
- }),
497
- audit('F5AF', 'SHOULD use 200 status code if parameters are invalid when accepting application/json', async () => {
498
- const res = await fetchFn(await getUrl(opts.url), {
499
- method: 'POST',
500
- headers: {
501
- 'content-type': 'application/json',
502
- accept: 'application/json',
503
- },
504
- body: JSON.stringify({
505
- qeury: /* typo */ '{ __typename }',
506
- }),
507
- });
508
- ressert(res).status.toBe(200);
409
+ ressert(res).status.toBeBetween(400, 499);
509
410
  }),
510
- audit('572B', 'SHOULD use 200 status code on document parsing failure when accepting application/json', async () => {
411
+ audit('BCF8', 'MAY use 400 status code on JSON parsing failure', async () => {
511
412
  const res = await fetchFn(await getUrl(opts.url), {
512
413
  method: 'POST',
513
414
  headers: {
514
415
  'content-type': 'application/json',
515
- accept: 'application/json',
516
416
  },
517
- body: JSON.stringify({ query: '{' }),
417
+ body: '{ "not a JSON',
518
418
  });
519
- ressert(res).status.toBe(200);
419
+ ressert(res).status.toBe(400);
520
420
  }),
521
- audit('FDE2', 'SHOULD use 200 status code on document validation failure when accepting application/json', async () => {
421
+ audit('8764', 'MAY use 4xx or 5xx status codes if parameters are invalid', async () => {
522
422
  const res = await fetchFn(await getUrl(opts.url), {
523
423
  method: 'POST',
524
424
  headers: {
525
425
  'content-type': 'application/json',
526
- accept: 'application/json',
527
426
  },
528
427
  body: JSON.stringify({
529
- query: '{ 8f31403dfe404bccbb0e835f2629c6a7 }', // making sure the field doesnt exist
428
+ qeury /* typo */: '{ __typename }',
530
429
  }),
531
430
  });
532
- ressert(res).status.toBe(200);
431
+ ressert(res).status.toBeBetween(400, 599);
533
432
  }),
534
- // Response application/graphql-response+json
535
- audit(
536
- // TODO: convert to MUST after watershed
537
- '60AA', 'SHOULD use 4xx or 5xx status codes on JSON parsing failure when accepting application/graphql-response+json', async () => {
433
+ audit('3E3A', 'MAY use 400 status code if parameters are invalid', async () => {
538
434
  const res = await fetchFn(await getUrl(opts.url), {
539
435
  method: 'POST',
540
436
  headers: {
541
437
  'content-type': 'application/json',
542
- accept: 'application/graphql-response+json',
543
438
  },
544
- body: '{ "not a JSON',
545
- });
546
- ressert(res).status.toBeBetween(400, 499);
547
- }),
548
- audit('2163', 'SHOULD use 400 status code on JSON parsing failure when accepting application/graphql-response+json', async () => {
549
- const res = await fetchFn(await getUrl(opts.url), {
550
- method: 'POST',
551
- headers: {
552
- 'content-type': 'application/json',
553
- accept: 'application/graphql-response+json',
554
- },
555
- body: '{ "not a JSON',
439
+ body: JSON.stringify({
440
+ qeury: /* typo */ '{ __typename }',
441
+ }),
556
442
  });
557
443
  ressert(res).status.toBe(400);
558
444
  }),
559
- audit(
560
- // TODO: convert to MUST after watershed
561
- '3E36', 'SHOULD use 4xx or 5xx status codes if parameters are invalid when accepting application/graphql-response+json', async () => {
445
+ // TODO: audit('39AA', 'MUST accept a map for the {extensions} parameter'),
446
+ // Response application/json
447
+ audit('572B', 'SHOULD use 200 status code on document parsing failure when accepting application/json', async () => {
562
448
  const res = await fetchFn(await getUrl(opts.url), {
563
449
  method: 'POST',
564
450
  headers: {
565
451
  'content-type': 'application/json',
566
- accept: 'application/graphql-response+json',
452
+ accept: 'application/json',
567
453
  },
568
- body: JSON.stringify({
569
- qeury /* typo */: '{ __typename }',
570
- }),
454
+ body: JSON.stringify({ query: '{' }),
571
455
  });
572
- ressert(res).status.toBeBetween(400, 599);
456
+ ressert(res).status.toBe(200);
573
457
  }),
574
- audit('17C5', 'SHOULD use 400 status code if parameters are invalid when accepting application/graphql-response+json', async () => {
458
+ audit('FDE2', 'SHOULD use 200 status code on document validation failure when accepting application/json', async () => {
575
459
  const res = await fetchFn(await getUrl(opts.url), {
576
460
  method: 'POST',
577
461
  headers: {
578
462
  'content-type': 'application/json',
579
- accept: 'application/graphql-response+json',
463
+ accept: 'application/json',
580
464
  },
581
465
  body: JSON.stringify({
582
- qeury: /* typo */ '{ __typename }',
466
+ query: '{ 8f31403dfe404bccbb0e835f2629c6a7 }', // making sure the field doesnt exist
583
467
  }),
584
468
  });
585
- ressert(res).status.toBe(400);
469
+ ressert(res).status.toBe(200);
586
470
  }),
587
- audit('34D6', 'SHOULD not contain the data entry if parameters are invalid when accepting application/graphql-response+json', async () => {
471
+ audit('7B9B', 'SHOULD use a status code of 200 on variable coercion failure when accepting application/json', async () => {
588
472
  const res = await fetchFn(await getUrl(opts.url), {
589
473
  method: 'POST',
590
474
  headers: {
591
475
  'content-type': 'application/json',
592
- accept: 'application/graphql-response+json',
476
+ accept: 'application/json',
593
477
  },
594
478
  body: JSON.stringify({
595
- qeury: /* typo */ '{ __typename }',
479
+ query: 'query CoerceFailure($id: ID!){ __typename }',
480
+ variables: { id: null },
596
481
  }),
597
482
  });
598
- await ressert(res).bodyAsExecutionResult.data.toBe(undefined);
483
+ ressert(res).status.toBe(200);
599
484
  }),
485
+ // Response application/graphql-response+json
600
486
  audit(
601
487
  // TODO: convert to MUST after watershed
602
488
  '865D', 'SHOULD use 4xx or 5xx status codes on document parsing failure when accepting application/graphql-response+json', async () => {
@@ -679,6 +565,20 @@ export function serverAudits(opts) {
679
565
  });
680
566
  await ressert(res).bodyAsExecutionResult.data.toBe(undefined);
681
567
  }),
568
+ audit('86EE', 'SHOULD use a status code of 400 on variable coercion failure when accepting application/graphql-response+json', async () => {
569
+ const res = await fetchFn(await getUrl(opts.url), {
570
+ method: 'POST',
571
+ headers: {
572
+ 'content-type': 'application/json',
573
+ accept: 'application/graphql-response+json',
574
+ },
575
+ body: JSON.stringify({
576
+ query: 'query CoerceFailure($id: ID!){ __typename }',
577
+ variables: { id: null },
578
+ }),
579
+ });
580
+ ressert(res).status.toBe(400);
581
+ }),
682
582
  // TODO: how to fail and have the data entry?
683
583
  // audit('EE52', 'MUST use 2xx status code if response contains the data entry and it is not null when accepting application/graphql-response+json'),
684
584
  // TODO: how to make an unauthorized request?
@@ -4,8 +4,8 @@
4
4
  *
5
5
  */
6
6
  import type { ExecutionResult } from 'graphql';
7
- import { Audit, AuditName } from './common';
8
- export * from '../utils';
7
+ import { Audit, AuditName } from './common.mjs';
8
+ export * from '../utils.mjs';
9
9
  /**
10
10
  * Wrap and prepare an audit for testing.
11
11
  *
@@ -48,10 +48,13 @@ function audit(id, name, fn) {
48
48
  id,
49
49
  name,
50
50
  status: name.startsWith('MUST')
51
- ? // only failing MUSTs are considered errors
51
+ ? // failing MUSTs are considered errors
52
52
  'error'
53
- : // everything else is optional and considered a warning
54
- 'warn',
53
+ : name.startsWith('SHOULD')
54
+ ? // recommendations are warnings
55
+ 'warn'
56
+ : // everything else is truly optional
57
+ 'notice',
55
58
  reason: err.reason,
56
59
  response: err.response,
57
60
  };
@@ -31,10 +31,13 @@ export function audit(id, name, fn) {
31
31
  id,
32
32
  name,
33
33
  status: name.startsWith('MUST')
34
- ? // only failing MUSTs are considered errors
34
+ ? // failing MUSTs are considered errors
35
35
  'error'
36
- : // everything else is optional and considered a warning
37
- 'warn',
36
+ : name.startsWith('SHOULD')
37
+ ? // recommendations are warnings
38
+ 'warn'
39
+ : // everything else is truly optional
40
+ 'notice',
38
41
  reason: err.reason,
39
42
  response: err.response,
40
43
  };
package/lib/client.d.mts CHANGED
@@ -4,9 +4,9 @@
4
4
  *
5
5
  */
6
6
  import type { ExecutionResult } from 'graphql';
7
- import { RequestParams, Sink } from './common';
7
+ import { RequestParams, Sink } from './common.mjs';
8
8
  /** This file is the entry point for browsers, re-export common elements. */
9
- export * from './common';
9
+ export * from './common.mjs';
10
10
  /** @category Client */
11
11
  export interface ClientOptions {
12
12
  /**
package/lib/handler.d.mts CHANGED
@@ -4,7 +4,7 @@
4
4
  *
5
5
  */
6
6
  import { ExecutionArgs, ExecutionResult, GraphQLSchema, validate as graphqlValidate, ValidationRule, execute as graphqlExecute, parse as graphqlParse, getOperationAST as graphqlGetOperationAST, GraphQLError } from 'graphql';
7
- import { RequestParams } from './common';
7
+ import { RequestParams } from './common.mjs';
8
8
  /**
9
9
  * The incoming request headers the implementing server should provide.
10
10
  *
@@ -101,6 +101,12 @@ export declare function isResponse(val: unknown): val is Response;
101
101
  * @category Server
102
102
  */
103
103
  export type OperationContext = Record<PropertyKey, unknown> | symbol | number | string | boolean | undefined | null;
104
+ /**
105
+ * The (GraphQL) error formatter function.
106
+ *
107
+ * @category Server
108
+ */
109
+ export type FormatError = (err: Readonly<GraphQLError | Error>) => GraphQLError | Error;
104
110
  /** @category Server */
105
111
  export type OperationArgs<Context extends OperationContext = undefined> = ExecutionArgs & {
106
112
  contextValue?: Context;
@@ -215,6 +221,14 @@ export interface HandlerOptions<RequestRaw = unknown, RequestContext = unknown,
215
221
  * further execution.
216
222
  */
217
223
  onOperation?: (req: Request<RequestRaw, RequestContext>, args: OperationArgs<Context>, result: ExecutionResult) => Promise<ExecutionResult | Response | void> | ExecutionResult | Response | void;
224
+ /**
225
+ * Format handled errors to your satisfaction. Either GraphQL errors
226
+ * or safe request processing errors are meant by "handleded errors".
227
+ *
228
+ * If multiple errors have occured, all of them will be mapped using
229
+ * this formatter.
230
+ */
231
+ formatError?: FormatError;
218
232
  }
219
233
  /**
220
234
  * The ready-to-use handler. Simply plug it in your favourite HTTP framework
@@ -246,7 +260,7 @@ export type Handler<RequestRaw = unknown, RequestContext = unknown> = (req: Requ
246
260
  * ```js
247
261
  * import http from 'http';
248
262
  * import { createHandler } from 'graphql-http';
249
- * import { schema } from './my-graphql-schema';
263
+ * import { schema } from './my-graphql-schema/index.mjs';
250
264
  *
251
265
  * // Create the GraphQL over HTTP handler
252
266
  * const handler = createHandler({ schema });
@@ -301,9 +315,12 @@ export declare function getAcceptableMediaType(acceptHeader: string | null | und
301
315
  *
302
316
  * If the first argument is an `ExecutionResult`, the operation will be treated as "successful".
303
317
  *
304
- * If the first argument is _any_ object without the `data` field, it will be treated as an error (as per the spec)
305
- * and the response will be constructed with the help of `acceptedMediaType` complying with the GraphQL over HTTP spec.
318
+ * If the first argument is (an array of) `GraphQLError`, or an `ExecutionResult` without the `data` field, it will be treated
319
+ * the response will be constructed with the help of `acceptedMediaType` complying with the GraphQL over HTTP spec.
320
+ *
321
+ * If the first argument is an `Error`, the operation will be treated as a bad request responding with `400: Bad Request` and the
322
+ * error will be present in the `ExecutionResult` style.
306
323
  *
307
324
  * @category Server
308
325
  */
309
- export declare function makeResponse(resultOrErrors: Readonly<ExecutionResult> | Readonly<GraphQLError[]> | Readonly<GraphQLError>, acceptedMediaType: AcceptableMediaType): Response;
326
+ export declare function makeResponse(resultOrErrors: Readonly<ExecutionResult> | Readonly<GraphQLError[]> | Readonly<GraphQLError> | Readonly<Error>, acceptedMediaType: AcceptableMediaType, formatError: FormatError): Response;
package/lib/handler.d.ts CHANGED
@@ -101,6 +101,12 @@ export declare function isResponse(val: unknown): val is Response;
101
101
  * @category Server
102
102
  */
103
103
  export type OperationContext = Record<PropertyKey, unknown> | symbol | number | string | boolean | undefined | null;
104
+ /**
105
+ * The (GraphQL) error formatter function.
106
+ *
107
+ * @category Server
108
+ */
109
+ export type FormatError = (err: Readonly<GraphQLError | Error>) => GraphQLError | Error;
104
110
  /** @category Server */
105
111
  export type OperationArgs<Context extends OperationContext = undefined> = ExecutionArgs & {
106
112
  contextValue?: Context;
@@ -215,6 +221,14 @@ export interface HandlerOptions<RequestRaw = unknown, RequestContext = unknown,
215
221
  * further execution.
216
222
  */
217
223
  onOperation?: (req: Request<RequestRaw, RequestContext>, args: OperationArgs<Context>, result: ExecutionResult) => Promise<ExecutionResult | Response | void> | ExecutionResult | Response | void;
224
+ /**
225
+ * Format handled errors to your satisfaction. Either GraphQL errors
226
+ * or safe request processing errors are meant by "handleded errors".
227
+ *
228
+ * If multiple errors have occured, all of them will be mapped using
229
+ * this formatter.
230
+ */
231
+ formatError?: FormatError;
218
232
  }
219
233
  /**
220
234
  * The ready-to-use handler. Simply plug it in your favourite HTTP framework
@@ -301,9 +315,12 @@ export declare function getAcceptableMediaType(acceptHeader: string | null | und
301
315
  *
302
316
  * If the first argument is an `ExecutionResult`, the operation will be treated as "successful".
303
317
  *
304
- * If the first argument is _any_ object without the `data` field, it will be treated as an error (as per the spec)
305
- * and the response will be constructed with the help of `acceptedMediaType` complying with the GraphQL over HTTP spec.
318
+ * If the first argument is (an array of) `GraphQLError`, or an `ExecutionResult` without the `data` field, it will be treated
319
+ * the response will be constructed with the help of `acceptedMediaType` complying with the GraphQL over HTTP spec.
320
+ *
321
+ * If the first argument is an `Error`, the operation will be treated as a bad request responding with `400: Bad Request` and the
322
+ * error will be present in the `ExecutionResult` style.
306
323
  *
307
324
  * @category Server
308
325
  */
309
- export declare function makeResponse(resultOrErrors: Readonly<ExecutionResult> | Readonly<GraphQLError[]> | Readonly<GraphQLError>, acceptedMediaType: AcceptableMediaType): Response;
326
+ export declare function makeResponse(resultOrErrors: Readonly<ExecutionResult> | Readonly<GraphQLError[]> | Readonly<GraphQLError> | Readonly<Error>, acceptedMediaType: AcceptableMediaType, formatError: FormatError): Response;