graphql-http 1.20.0 → 1.22.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.
- package/README.md +13 -1
- package/lib/handler.d.mts +19 -2
- package/lib/handler.d.ts +19 -2
- package/lib/handler.js +145 -115
- package/lib/handler.mjs +137 -108
- package/lib/use/@netlify/functions.d.mts +14 -0
- package/lib/use/@netlify/functions.d.ts +14 -0
- package/lib/use/@netlify/functions.js +37 -0
- package/lib/use/@netlify/functions.mjs +33 -0
- package/lib/use/express.d.mts +41 -0
- package/lib/use/express.d.ts +41 -0
- package/lib/use/express.js +72 -19
- package/lib/use/express.mjs +71 -19
- package/lib/use/fastify.d.mts +42 -1
- package/lib/use/fastify.d.ts +42 -1
- package/lib/use/fastify.js +68 -11
- package/lib/use/fastify.mjs +67 -11
- package/lib/use/fetch.d.mts +50 -0
- package/lib/use/fetch.d.ts +50 -0
- package/lib/use/fetch.js +74 -9
- package/lib/use/fetch.mjs +73 -9
- package/lib/use/http.d.mts +44 -0
- package/lib/use/http.d.ts +44 -0
- package/lib/use/http.js +75 -13
- package/lib/use/http.mjs +74 -13
- package/lib/use/http2.d.mts +56 -0
- package/lib/use/http2.d.ts +56 -0
- package/lib/use/http2.js +93 -13
- package/lib/use/http2.mjs +92 -13
- package/lib/use/koa.d.mts +47 -1
- package/lib/use/koa.d.ts +47 -1
- package/lib/use/koa.js +84 -1
- package/lib/use/koa.mjs +83 -1
- package/lib/use/uWebSockets.d.mts +55 -0
- package/lib/use/uWebSockets.d.ts +55 -0
- package/lib/use/uWebSockets.js +109 -31
- package/lib/use/uWebSockets.mjs +108 -31
- package/lib/utils.d.mts +0 -7
- package/lib/utils.d.ts +0 -7
- package/lib/utils.js +1 -31
- package/lib/utils.mjs +0 -27
- package/package.json +28 -32
- package/umd/graphql-http-audits.js +2 -2
- package/umd/graphql-http-audits.min.js +1 -1
- package/umd/graphql-http-audits.min.js.gz +0 -0
- package/umd/graphql-http.js +3 -2
- package/umd/graphql-http.min.js +1 -1
- package/umd/graphql-http.min.js.gz +0 -0
package/lib/handler.mjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
6
|
import { validate as graphqlValidate, specifiedRules, execute as graphqlExecute, parse as graphqlParse, getOperationAST as graphqlGetOperationAST, GraphQLError, } from 'graphql';
|
|
7
|
-
import {
|
|
7
|
+
import { isAsyncIterable, isExecutionResult, isObject } from './utils.mjs';
|
|
8
8
|
/** Checks whether the passed value is the `graphql-http` server agnostic response. */
|
|
9
9
|
function isResponse(val) {
|
|
10
10
|
// TODO: make sure the contents of init match ResponseInit
|
|
@@ -12,6 +12,116 @@ function isResponse(val) {
|
|
|
12
12
|
(typeof val[0] === 'string' || val[0] === null) &&
|
|
13
13
|
isObject(val[1]));
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* The GraphQL over HTTP spec compliant request parser for an incoming GraphQL request.
|
|
17
|
+
* It parses and validates the request itself, including the request method and the
|
|
18
|
+
* content-type of the body.
|
|
19
|
+
*
|
|
20
|
+
* If the HTTP request itself is invalid or malformed, the function will return an
|
|
21
|
+
* appropriate {@link Response}.
|
|
22
|
+
*
|
|
23
|
+
* If the HTTP request is valid, but is not a well-formatted GraphQL request, the
|
|
24
|
+
* function will throw an error and it is up to the user to handle and respond as
|
|
25
|
+
* they see fit.
|
|
26
|
+
*
|
|
27
|
+
* @category Server
|
|
28
|
+
*/
|
|
29
|
+
export async function parseRequestParams(req) {
|
|
30
|
+
var _a, _b;
|
|
31
|
+
const method = req.method;
|
|
32
|
+
if (method !== 'GET' && method !== 'POST') {
|
|
33
|
+
return [
|
|
34
|
+
null,
|
|
35
|
+
{
|
|
36
|
+
status: 405,
|
|
37
|
+
statusText: 'Method Not Allowed',
|
|
38
|
+
headers: {
|
|
39
|
+
allow: 'GET, POST',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
}
|
|
44
|
+
const [mediaType, charset = 'charset=utf-8', // utf-8 is assumed when not specified. this parameter is either "charset" or "boundary" (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Length)
|
|
45
|
+
] = (getHeader(req, 'content-type') || '')
|
|
46
|
+
.replace(/\s/g, '')
|
|
47
|
+
.toLowerCase()
|
|
48
|
+
.split(';');
|
|
49
|
+
const partParams = {};
|
|
50
|
+
switch (true) {
|
|
51
|
+
case method === 'GET': {
|
|
52
|
+
// TODO: what if content-type is specified and is not application/x-www-form-urlencoded?
|
|
53
|
+
try {
|
|
54
|
+
const [, search] = req.url.split('?');
|
|
55
|
+
const searchParams = new URLSearchParams(search);
|
|
56
|
+
partParams.operationName =
|
|
57
|
+
(_a = searchParams.get('operationName')) !== null && _a !== void 0 ? _a : undefined;
|
|
58
|
+
partParams.query = (_b = searchParams.get('query')) !== null && _b !== void 0 ? _b : undefined;
|
|
59
|
+
const variables = searchParams.get('variables');
|
|
60
|
+
if (variables)
|
|
61
|
+
partParams.variables = JSON.parse(variables);
|
|
62
|
+
const extensions = searchParams.get('extensions');
|
|
63
|
+
if (extensions)
|
|
64
|
+
partParams.extensions = JSON.parse(extensions);
|
|
65
|
+
}
|
|
66
|
+
catch (_c) {
|
|
67
|
+
throw new Error('Unparsable URL');
|
|
68
|
+
}
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
case method === 'POST' &&
|
|
72
|
+
mediaType === 'application/json' &&
|
|
73
|
+
charset === 'charset=utf-8':
|
|
74
|
+
{
|
|
75
|
+
if (!req.body) {
|
|
76
|
+
throw new Error('Missing body');
|
|
77
|
+
}
|
|
78
|
+
let data;
|
|
79
|
+
try {
|
|
80
|
+
const body = typeof req.body === 'function' ? await req.body() : req.body;
|
|
81
|
+
data = typeof body === 'string' ? JSON.parse(body) : body;
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
throw new Error('Unparsable JSON body');
|
|
85
|
+
}
|
|
86
|
+
if (!isObject(data)) {
|
|
87
|
+
throw new Error('JSON body must be an object');
|
|
88
|
+
}
|
|
89
|
+
partParams.operationName = data.operationName;
|
|
90
|
+
partParams.query = data.query;
|
|
91
|
+
partParams.variables = data.variables;
|
|
92
|
+
partParams.extensions = data.extensions;
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
default: // graphql-http doesnt support any other content type
|
|
96
|
+
return [
|
|
97
|
+
null,
|
|
98
|
+
{
|
|
99
|
+
status: 415,
|
|
100
|
+
statusText: 'Unsupported Media Type',
|
|
101
|
+
},
|
|
102
|
+
];
|
|
103
|
+
}
|
|
104
|
+
if (partParams.query == null)
|
|
105
|
+
throw new Error('Missing query');
|
|
106
|
+
if (typeof partParams.query !== 'string')
|
|
107
|
+
throw new Error('Invalid query');
|
|
108
|
+
if (partParams.variables != null &&
|
|
109
|
+
(typeof partParams.variables !== 'object' ||
|
|
110
|
+
Array.isArray(partParams.variables))) {
|
|
111
|
+
throw new Error('Invalid variables');
|
|
112
|
+
}
|
|
113
|
+
if (partParams.operationName != null &&
|
|
114
|
+
typeof partParams.operationName !== 'string') {
|
|
115
|
+
throw new Error('Invalid operationName');
|
|
116
|
+
}
|
|
117
|
+
if (partParams.extensions != null &&
|
|
118
|
+
(typeof partParams.extensions !== 'object' ||
|
|
119
|
+
Array.isArray(partParams.extensions))) {
|
|
120
|
+
throw new Error('Invalid extensions');
|
|
121
|
+
}
|
|
122
|
+
// request parameters are checked and now complete
|
|
123
|
+
return partParams;
|
|
124
|
+
}
|
|
15
125
|
/**
|
|
16
126
|
* Makes a GraphQL over HTTP spec compliant server handler. The handler can
|
|
17
127
|
* be used with your favorite server library.
|
|
@@ -68,7 +178,7 @@ function isResponse(val) {
|
|
|
68
178
|
* @category Server
|
|
69
179
|
*/
|
|
70
180
|
export function createHandler(options) {
|
|
71
|
-
const { schema, context, validate = graphqlValidate, validationRules = [], execute = graphqlExecute, parse = graphqlParse, getOperationAST = graphqlGetOperationAST, rootValue, onSubscribe, onOperation, formatError = (err) => err, parseRequestParams =
|
|
181
|
+
const { schema, context, validate = graphqlValidate, validationRules = [], execute = graphqlExecute, parse = graphqlParse, getOperationAST = graphqlGetOperationAST, rootValue, onSubscribe, onOperation, formatError = (err) => err, parseRequestParams: optionsParseRequestParams = parseRequestParams, } = options;
|
|
72
182
|
return async function handler(req) {
|
|
73
183
|
let acceptedMediaType = null;
|
|
74
184
|
const accepts = (getHeader(req, 'accept') || '*/*')
|
|
@@ -108,9 +218,9 @@ export function createHandler(options) {
|
|
|
108
218
|
}
|
|
109
219
|
let params;
|
|
110
220
|
try {
|
|
111
|
-
let paramsOrRes = await
|
|
221
|
+
let paramsOrRes = await optionsParseRequestParams(req);
|
|
112
222
|
if (!paramsOrRes)
|
|
113
|
-
paramsOrRes = await
|
|
223
|
+
paramsOrRes = await parseRequestParams(req);
|
|
114
224
|
if (isResponse(paramsOrRes))
|
|
115
225
|
return paramsOrRes;
|
|
116
226
|
params = paramsOrRes;
|
|
@@ -218,110 +328,6 @@ export function createHandler(options) {
|
|
|
218
328
|
return makeResponse(result, acceptedMediaType, formatError);
|
|
219
329
|
};
|
|
220
330
|
}
|
|
221
|
-
/**
|
|
222
|
-
* The default request params parser. Used when no custom one is provided or if it
|
|
223
|
-
* returns nothing.
|
|
224
|
-
*
|
|
225
|
-
* Read more about it in {@link ParseRequestParams}.
|
|
226
|
-
*
|
|
227
|
-
* TODO: should graphql-http itself care about content-encoding? I'd say unzipping should happen before handler is reached
|
|
228
|
-
*/
|
|
229
|
-
async function defaultParseRequestParams(req) {
|
|
230
|
-
var _a, _b;
|
|
231
|
-
const method = req.method;
|
|
232
|
-
if (method !== 'GET' && method !== 'POST') {
|
|
233
|
-
return [
|
|
234
|
-
null,
|
|
235
|
-
{
|
|
236
|
-
status: 405,
|
|
237
|
-
statusText: 'Method Not Allowed',
|
|
238
|
-
headers: {
|
|
239
|
-
allow: 'GET, POST',
|
|
240
|
-
},
|
|
241
|
-
},
|
|
242
|
-
];
|
|
243
|
-
}
|
|
244
|
-
const [mediaType, charset = 'charset=utf-8', // utf-8 is assumed when not specified. this parameter is either "charset" or "boundary" (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Length)
|
|
245
|
-
] = (getHeader(req, 'content-type') || '')
|
|
246
|
-
.replace(/\s/g, '')
|
|
247
|
-
.toLowerCase()
|
|
248
|
-
.split(';');
|
|
249
|
-
const partParams = {};
|
|
250
|
-
switch (true) {
|
|
251
|
-
case method === 'GET': {
|
|
252
|
-
// TODO: what if content-type is specified and is not application/x-www-form-urlencoded?
|
|
253
|
-
try {
|
|
254
|
-
const [, search] = req.url.split('?');
|
|
255
|
-
const searchParams = new URLSearchParams(search);
|
|
256
|
-
partParams.operationName =
|
|
257
|
-
(_a = searchParams.get('operationName')) !== null && _a !== void 0 ? _a : undefined;
|
|
258
|
-
partParams.query = (_b = searchParams.get('query')) !== null && _b !== void 0 ? _b : undefined;
|
|
259
|
-
const variables = searchParams.get('variables');
|
|
260
|
-
if (variables)
|
|
261
|
-
partParams.variables = JSON.parse(variables);
|
|
262
|
-
const extensions = searchParams.get('extensions');
|
|
263
|
-
if (extensions)
|
|
264
|
-
partParams.extensions = JSON.parse(extensions);
|
|
265
|
-
}
|
|
266
|
-
catch (_c) {
|
|
267
|
-
throw new Error('Unparsable URL');
|
|
268
|
-
}
|
|
269
|
-
break;
|
|
270
|
-
}
|
|
271
|
-
case method === 'POST' &&
|
|
272
|
-
mediaType === 'application/json' &&
|
|
273
|
-
charset === 'charset=utf-8':
|
|
274
|
-
{
|
|
275
|
-
if (!req.body) {
|
|
276
|
-
throw new Error('Missing body');
|
|
277
|
-
}
|
|
278
|
-
let data;
|
|
279
|
-
try {
|
|
280
|
-
const body = typeof req.body === 'function' ? await req.body() : req.body;
|
|
281
|
-
data = typeof body === 'string' ? JSON.parse(body) : body;
|
|
282
|
-
}
|
|
283
|
-
catch (err) {
|
|
284
|
-
throw new Error('Unparsable JSON body');
|
|
285
|
-
}
|
|
286
|
-
if (!isObject(data)) {
|
|
287
|
-
throw new Error('JSON body must be an object');
|
|
288
|
-
}
|
|
289
|
-
partParams.operationName = data.operationName;
|
|
290
|
-
partParams.query = data.query;
|
|
291
|
-
partParams.variables = data.variables;
|
|
292
|
-
partParams.extensions = data.extensions;
|
|
293
|
-
break;
|
|
294
|
-
}
|
|
295
|
-
default: // graphql-http doesnt support any other content type
|
|
296
|
-
return [
|
|
297
|
-
null,
|
|
298
|
-
{
|
|
299
|
-
status: 415,
|
|
300
|
-
statusText: 'Unsupported Media Type',
|
|
301
|
-
},
|
|
302
|
-
];
|
|
303
|
-
}
|
|
304
|
-
if (partParams.query == null)
|
|
305
|
-
throw new Error('Missing query');
|
|
306
|
-
if (typeof partParams.query !== 'string')
|
|
307
|
-
throw new Error('Invalid query');
|
|
308
|
-
if (partParams.variables != null &&
|
|
309
|
-
(typeof partParams.variables !== 'object' ||
|
|
310
|
-
Array.isArray(partParams.variables))) {
|
|
311
|
-
throw new Error('Invalid variables');
|
|
312
|
-
}
|
|
313
|
-
if (partParams.operationName != null &&
|
|
314
|
-
typeof partParams.operationName !== 'string') {
|
|
315
|
-
throw new Error('Invalid operationName');
|
|
316
|
-
}
|
|
317
|
-
if (partParams.extensions != null &&
|
|
318
|
-
(typeof partParams.extensions !== 'object' ||
|
|
319
|
-
Array.isArray(partParams.extensions))) {
|
|
320
|
-
throw new Error('Invalid extensions');
|
|
321
|
-
}
|
|
322
|
-
// request parameters are checked and now complete
|
|
323
|
-
return partParams;
|
|
324
|
-
}
|
|
325
331
|
/**
|
|
326
332
|
* Creates an appropriate GraphQL over HTTP response following the provided arguments.
|
|
327
333
|
*
|
|
@@ -391,3 +397,26 @@ function getHeader(req, key) {
|
|
|
391
397
|
}
|
|
392
398
|
return Object(req.headers)[key];
|
|
393
399
|
}
|
|
400
|
+
function areGraphQLErrors(obj) {
|
|
401
|
+
return (Array.isArray(obj) &&
|
|
402
|
+
obj.length > 0 &&
|
|
403
|
+
// if one item in the array is a GraphQLError, we're good
|
|
404
|
+
obj.some(isGraphQLError));
|
|
405
|
+
}
|
|
406
|
+
function isGraphQLError(obj) {
|
|
407
|
+
return obj instanceof GraphQLError;
|
|
408
|
+
}
|
|
409
|
+
function jsonErrorReplacer(_key,
|
|
410
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
411
|
+
val) {
|
|
412
|
+
if (val instanceof Error &&
|
|
413
|
+
// GraphQL errors implement their own stringer
|
|
414
|
+
!isGraphQLError(val)) {
|
|
415
|
+
return {
|
|
416
|
+
// name: val.name, name is included in message
|
|
417
|
+
message: val.message,
|
|
418
|
+
// stack: val.stack, can leak sensitive details
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
return val;
|
|
422
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { HandlerOptions as RawHandlerOptions, OperationContext } from '../../handler/index.mjs';
|
|
2
|
+
import type { Handler as NetlifyHandler, HandlerEvent as NetlifyHandlerEvent, HandlerContext as NetlifyHandlerContext } from '@netlify/functions';
|
|
3
|
+
/**
|
|
4
|
+
* Handler options when using the netlify adapter
|
|
5
|
+
*
|
|
6
|
+
* @category Server/@netlify/functions
|
|
7
|
+
*/
|
|
8
|
+
export type HandlerOptions<Context extends OperationContext = undefined> = RawHandlerOptions<NetlifyHandlerEvent, NetlifyHandlerContext, Context>;
|
|
9
|
+
/**
|
|
10
|
+
* Create a GraphQL over HTTP spec compliant request handler for netlify functions
|
|
11
|
+
*
|
|
12
|
+
* @category Server/@netlify/functions
|
|
13
|
+
*/
|
|
14
|
+
export declare function createHandler<Context extends OperationContext = undefined>(options: HandlerOptions<Context>): NetlifyHandler;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { HandlerOptions as RawHandlerOptions, OperationContext } from '../../handler';
|
|
2
|
+
import type { Handler as NetlifyHandler, HandlerEvent as NetlifyHandlerEvent, HandlerContext as NetlifyHandlerContext } from '@netlify/functions';
|
|
3
|
+
/**
|
|
4
|
+
* Handler options when using the netlify adapter
|
|
5
|
+
*
|
|
6
|
+
* @category Server/@netlify/functions
|
|
7
|
+
*/
|
|
8
|
+
export type HandlerOptions<Context extends OperationContext = undefined> = RawHandlerOptions<NetlifyHandlerEvent, NetlifyHandlerContext, Context>;
|
|
9
|
+
/**
|
|
10
|
+
* Create a GraphQL over HTTP spec compliant request handler for netlify functions
|
|
11
|
+
*
|
|
12
|
+
* @category Server/@netlify/functions
|
|
13
|
+
*/
|
|
14
|
+
export declare function createHandler<Context extends OperationContext = undefined>(options: HandlerOptions<Context>): NetlifyHandler;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createHandler = void 0;
|
|
4
|
+
const handler_1 = require("../../handler");
|
|
5
|
+
/**
|
|
6
|
+
* Create a GraphQL over HTTP spec compliant request handler for netlify functions
|
|
7
|
+
*
|
|
8
|
+
* @category Server/@netlify/functions
|
|
9
|
+
*/
|
|
10
|
+
function createHandler(options) {
|
|
11
|
+
const handler = (0, handler_1.createHandler)(options);
|
|
12
|
+
return async function handleRequest(req, ctx) {
|
|
13
|
+
try {
|
|
14
|
+
const [body, init] = await handler({
|
|
15
|
+
method: req.httpMethod,
|
|
16
|
+
url: req.rawUrl,
|
|
17
|
+
headers: req.headers,
|
|
18
|
+
body: req.body,
|
|
19
|
+
raw: req,
|
|
20
|
+
context: ctx,
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
// if body is null, return undefined
|
|
24
|
+
body: body !== null && body !== void 0 ? body : undefined,
|
|
25
|
+
statusCode: init.status,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
// The handler shouldnt throw errors.
|
|
30
|
+
// If you wish to handle them differently, consider implementing your own request handler.
|
|
31
|
+
console.error('Internal error occurred during request handling. ' +
|
|
32
|
+
'Please check your implementation.', err);
|
|
33
|
+
return { statusCode: 500 };
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
exports.createHandler = createHandler;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createHandler as createRawHandler, } from '../../handler.mjs';
|
|
2
|
+
/**
|
|
3
|
+
* Create a GraphQL over HTTP spec compliant request handler for netlify functions
|
|
4
|
+
*
|
|
5
|
+
* @category Server/@netlify/functions
|
|
6
|
+
*/
|
|
7
|
+
export function createHandler(options) {
|
|
8
|
+
const handler = createRawHandler(options);
|
|
9
|
+
return async function handleRequest(req, ctx) {
|
|
10
|
+
try {
|
|
11
|
+
const [body, init] = await handler({
|
|
12
|
+
method: req.httpMethod,
|
|
13
|
+
url: req.rawUrl,
|
|
14
|
+
headers: req.headers,
|
|
15
|
+
body: req.body,
|
|
16
|
+
raw: req,
|
|
17
|
+
context: ctx,
|
|
18
|
+
});
|
|
19
|
+
return {
|
|
20
|
+
// if body is null, return undefined
|
|
21
|
+
body: body !== null && body !== void 0 ? body : undefined,
|
|
22
|
+
statusCode: init.status,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
// The handler shouldnt throw errors.
|
|
27
|
+
// If you wish to handle them differently, consider implementing your own request handler.
|
|
28
|
+
console.error('Internal error occurred during request handling. ' +
|
|
29
|
+
'Please check your implementation.', err);
|
|
30
|
+
return { statusCode: 500 };
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
package/lib/use/express.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Request, Response, Handler } from 'express';
|
|
2
2
|
import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler.mjs';
|
|
3
|
+
import { RequestParams } from '../common.mjs';
|
|
3
4
|
/**
|
|
4
5
|
* The context in the request for the handler.
|
|
5
6
|
*
|
|
@@ -8,6 +9,46 @@ import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handle
|
|
|
8
9
|
export interface RequestContext {
|
|
9
10
|
res: Response;
|
|
10
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* The GraphQL over HTTP spec compliant request parser for an incoming GraphQL request.
|
|
14
|
+
*
|
|
15
|
+
* If the HTTP request _is not_ a [well-formatted GraphQL over HTTP request](https://graphql.github.io/graphql-over-http/draft/#sec-Request), the function will respond
|
|
16
|
+
* on the `Response` argument and return `null`.
|
|
17
|
+
*
|
|
18
|
+
* If the HTTP request _is_ a [well-formatted GraphQL over HTTP request](https://graphql.github.io/graphql-over-http/draft/#sec-Request), but is invalid or malformed,
|
|
19
|
+
* the function will throw an error and it is up to the user to handle and respond as they see fit.
|
|
20
|
+
*
|
|
21
|
+
* ```js
|
|
22
|
+
* import express from 'express'; // yarn add express
|
|
23
|
+
* import { parseRequestParams } from 'graphql-http/lib/use/express';
|
|
24
|
+
*
|
|
25
|
+
* const app = express();
|
|
26
|
+
* app.all('/graphql', async (req, res) => {
|
|
27
|
+
* try {
|
|
28
|
+
* const maybeParams = await parseRequestParams(req, res);
|
|
29
|
+
* if (!maybeParams) {
|
|
30
|
+
* // not a well-formatted GraphQL over HTTP request,
|
|
31
|
+
* // parser responded and there's nothing else to do
|
|
32
|
+
* return;
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* // well-formatted GraphQL over HTTP request,
|
|
36
|
+
* // with valid parameters
|
|
37
|
+
* res.writeHead(200).end(JSON.stringify(maybeParams, null, ' '));
|
|
38
|
+
* } catch (err) {
|
|
39
|
+
* // well-formatted GraphQL over HTTP request,
|
|
40
|
+
* // but with invalid parameters
|
|
41
|
+
* res.writeHead(400).end(err.message);
|
|
42
|
+
* }
|
|
43
|
+
* });
|
|
44
|
+
*
|
|
45
|
+
* app.listen({ port: 4000 });
|
|
46
|
+
* console.log('Listening to port 4000');
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @category Server/express
|
|
50
|
+
*/
|
|
51
|
+
export declare function parseRequestParams(req: Request, res: Response): Promise<RequestParams | null>;
|
|
11
52
|
/**
|
|
12
53
|
* Handler options when using the express adapter.
|
|
13
54
|
*
|
package/lib/use/express.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Request, Response, Handler } from 'express';
|
|
2
2
|
import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler';
|
|
3
|
+
import { RequestParams } from '../common';
|
|
3
4
|
/**
|
|
4
5
|
* The context in the request for the handler.
|
|
5
6
|
*
|
|
@@ -8,6 +9,46 @@ import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handle
|
|
|
8
9
|
export interface RequestContext {
|
|
9
10
|
res: Response;
|
|
10
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* The GraphQL over HTTP spec compliant request parser for an incoming GraphQL request.
|
|
14
|
+
*
|
|
15
|
+
* If the HTTP request _is not_ a [well-formatted GraphQL over HTTP request](https://graphql.github.io/graphql-over-http/draft/#sec-Request), the function will respond
|
|
16
|
+
* on the `Response` argument and return `null`.
|
|
17
|
+
*
|
|
18
|
+
* If the HTTP request _is_ a [well-formatted GraphQL over HTTP request](https://graphql.github.io/graphql-over-http/draft/#sec-Request), but is invalid or malformed,
|
|
19
|
+
* the function will throw an error and it is up to the user to handle and respond as they see fit.
|
|
20
|
+
*
|
|
21
|
+
* ```js
|
|
22
|
+
* import express from 'express'; // yarn add express
|
|
23
|
+
* import { parseRequestParams } from 'graphql-http/lib/use/express';
|
|
24
|
+
*
|
|
25
|
+
* const app = express();
|
|
26
|
+
* app.all('/graphql', async (req, res) => {
|
|
27
|
+
* try {
|
|
28
|
+
* const maybeParams = await parseRequestParams(req, res);
|
|
29
|
+
* if (!maybeParams) {
|
|
30
|
+
* // not a well-formatted GraphQL over HTTP request,
|
|
31
|
+
* // parser responded and there's nothing else to do
|
|
32
|
+
* return;
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* // well-formatted GraphQL over HTTP request,
|
|
36
|
+
* // with valid parameters
|
|
37
|
+
* res.writeHead(200).end(JSON.stringify(maybeParams, null, ' '));
|
|
38
|
+
* } catch (err) {
|
|
39
|
+
* // well-formatted GraphQL over HTTP request,
|
|
40
|
+
* // but with invalid parameters
|
|
41
|
+
* res.writeHead(400).end(err.message);
|
|
42
|
+
* }
|
|
43
|
+
* });
|
|
44
|
+
*
|
|
45
|
+
* app.listen({ port: 4000 });
|
|
46
|
+
* console.log('Listening to port 4000');
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @category Server/express
|
|
50
|
+
*/
|
|
51
|
+
export declare function parseRequestParams(req: Request, res: Response): Promise<RequestParams | null>;
|
|
11
52
|
/**
|
|
12
53
|
* Handler options when using the express adapter.
|
|
13
54
|
*
|
package/lib/use/express.js
CHANGED
|
@@ -1,7 +1,57 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createHandler = void 0;
|
|
3
|
+
exports.createHandler = exports.parseRequestParams = void 0;
|
|
4
4
|
const handler_1 = require("../handler");
|
|
5
|
+
/**
|
|
6
|
+
* The GraphQL over HTTP spec compliant request parser for an incoming GraphQL request.
|
|
7
|
+
*
|
|
8
|
+
* If the HTTP request _is not_ a [well-formatted GraphQL over HTTP request](https://graphql.github.io/graphql-over-http/draft/#sec-Request), the function will respond
|
|
9
|
+
* on the `Response` argument and return `null`.
|
|
10
|
+
*
|
|
11
|
+
* If the HTTP request _is_ a [well-formatted GraphQL over HTTP request](https://graphql.github.io/graphql-over-http/draft/#sec-Request), but is invalid or malformed,
|
|
12
|
+
* the function will throw an error and it is up to the user to handle and respond as they see fit.
|
|
13
|
+
*
|
|
14
|
+
* ```js
|
|
15
|
+
* import express from 'express'; // yarn add express
|
|
16
|
+
* import { parseRequestParams } from 'graphql-http/lib/use/express';
|
|
17
|
+
*
|
|
18
|
+
* const app = express();
|
|
19
|
+
* app.all('/graphql', async (req, res) => {
|
|
20
|
+
* try {
|
|
21
|
+
* const maybeParams = await parseRequestParams(req, res);
|
|
22
|
+
* if (!maybeParams) {
|
|
23
|
+
* // not a well-formatted GraphQL over HTTP request,
|
|
24
|
+
* // parser responded and there's nothing else to do
|
|
25
|
+
* return;
|
|
26
|
+
* }
|
|
27
|
+
*
|
|
28
|
+
* // well-formatted GraphQL over HTTP request,
|
|
29
|
+
* // with valid parameters
|
|
30
|
+
* res.writeHead(200).end(JSON.stringify(maybeParams, null, ' '));
|
|
31
|
+
* } catch (err) {
|
|
32
|
+
* // well-formatted GraphQL over HTTP request,
|
|
33
|
+
* // but with invalid parameters
|
|
34
|
+
* res.writeHead(400).end(err.message);
|
|
35
|
+
* }
|
|
36
|
+
* });
|
|
37
|
+
*
|
|
38
|
+
* app.listen({ port: 4000 });
|
|
39
|
+
* console.log('Listening to port 4000');
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @category Server/express
|
|
43
|
+
*/
|
|
44
|
+
async function parseRequestParams(req, res) {
|
|
45
|
+
const rawReq = toRequest(req, res);
|
|
46
|
+
const paramsOrRes = await (0, handler_1.parseRequestParams)(rawReq);
|
|
47
|
+
if (!('query' in paramsOrRes)) {
|
|
48
|
+
const [body, init] = paramsOrRes;
|
|
49
|
+
res.writeHead(init.status, init.statusText, init.headers).end(body);
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return paramsOrRes;
|
|
53
|
+
}
|
|
54
|
+
exports.parseRequestParams = parseRequestParams;
|
|
5
55
|
/**
|
|
6
56
|
* Create a GraphQL over HTTP spec compliant request handler for
|
|
7
57
|
* the express framework.
|
|
@@ -24,24 +74,7 @@ function createHandler(options) {
|
|
|
24
74
|
const handle = (0, handler_1.createHandler)(options);
|
|
25
75
|
return async function requestListener(req, res) {
|
|
26
76
|
try {
|
|
27
|
-
const [body, init] = await handle(
|
|
28
|
-
url: req.url,
|
|
29
|
-
method: req.method,
|
|
30
|
-
headers: req.headers,
|
|
31
|
-
body: () => {
|
|
32
|
-
if (req.body) {
|
|
33
|
-
// in case express has a body parser
|
|
34
|
-
return req.body;
|
|
35
|
-
}
|
|
36
|
-
return new Promise((resolve) => {
|
|
37
|
-
let body = '';
|
|
38
|
-
req.on('data', (chunk) => (body += chunk));
|
|
39
|
-
req.on('end', () => resolve(body));
|
|
40
|
-
});
|
|
41
|
-
},
|
|
42
|
-
raw: req,
|
|
43
|
-
context: { res },
|
|
44
|
-
});
|
|
77
|
+
const [body, init] = await handle(toRequest(req, res));
|
|
45
78
|
res.writeHead(init.status, init.statusText, init.headers).end(body);
|
|
46
79
|
}
|
|
47
80
|
catch (err) {
|
|
@@ -54,3 +87,23 @@ function createHandler(options) {
|
|
|
54
87
|
};
|
|
55
88
|
}
|
|
56
89
|
exports.createHandler = createHandler;
|
|
90
|
+
function toRequest(req, res) {
|
|
91
|
+
return {
|
|
92
|
+
url: req.url,
|
|
93
|
+
method: req.method,
|
|
94
|
+
headers: req.headers,
|
|
95
|
+
body: () => {
|
|
96
|
+
if (req.body) {
|
|
97
|
+
// in case express has a body parser
|
|
98
|
+
return req.body;
|
|
99
|
+
}
|
|
100
|
+
return new Promise((resolve) => {
|
|
101
|
+
let body = '';
|
|
102
|
+
req.on('data', (chunk) => (body += chunk));
|
|
103
|
+
req.on('end', () => resolve(body));
|
|
104
|
+
});
|
|
105
|
+
},
|
|
106
|
+
raw: req,
|
|
107
|
+
context: { res },
|
|
108
|
+
};
|
|
109
|
+
}
|