graphql-http
Advanced tools
Comparing version 1.21.0 to 1.22.0
@@ -105,5 +105,7 @@ /** | ||
/** | ||
* The request parser for an incoming GraphQL request. It parses and validates the | ||
* request itself, including the request method and the content-type of the body. | ||
* The request parser for an incoming GraphQL request in the handler. | ||
* | ||
* It should parse and validate the request itself, including the request method | ||
* and the content-type of the body. | ||
* | ||
* In case you are extending the server to handle more request types, this is the | ||
@@ -124,2 +126,17 @@ * perfect place to do so. | ||
export type ParseRequestParams<RequestRaw = unknown, RequestContext = unknown> = (req: Request<RequestRaw, RequestContext>) => Promise<RequestParams | Response | void> | RequestParams | Response | void; | ||
/** | ||
* The GraphQL over HTTP spec compliant request parser for an incoming GraphQL request. | ||
* It parses and validates the request itself, including the request method and the | ||
* content-type of the body. | ||
* | ||
* If the HTTP request itself is invalid or malformed, the function will return an | ||
* appropriate {@link Response}. | ||
* | ||
* If the HTTP request is valid, but is not a well-formatted GraphQL request, the | ||
* function will throw an error and it is up to the user to handle and respond as | ||
* they see fit. | ||
* | ||
* @category Server | ||
*/ | ||
export declare function parseRequestParams<RequestRaw = unknown, RequestContext = unknown>(req: Request<RequestRaw, RequestContext>): Promise<Response | RequestParams>; | ||
/** @category Server */ | ||
@@ -126,0 +143,0 @@ export type OperationArgs<Context extends OperationContext = undefined> = ExecutionArgs & { |
@@ -8,3 +8,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.createHandler = void 0; | ||
exports.createHandler = exports.parseRequestParams = void 0; | ||
const graphql_1 = require("graphql"); | ||
@@ -20,2 +20,113 @@ const utils_1 = require("./utils"); | ||
/** | ||
* The GraphQL over HTTP spec compliant request parser for an incoming GraphQL request. | ||
* It parses and validates the request itself, including the request method and the | ||
* content-type of the body. | ||
* | ||
* If the HTTP request itself is invalid or malformed, the function will return an | ||
* appropriate {@link Response}. | ||
* | ||
* If the HTTP request is valid, but is not a well-formatted GraphQL request, the | ||
* function will throw an error and it is up to the user to handle and respond as | ||
* they see fit. | ||
* | ||
* @category Server | ||
*/ | ||
async function parseRequestParams(req) { | ||
var _a, _b; | ||
const method = req.method; | ||
if (method !== 'GET' && method !== 'POST') { | ||
return [ | ||
null, | ||
{ | ||
status: 405, | ||
statusText: 'Method Not Allowed', | ||
headers: { | ||
allow: 'GET, POST', | ||
}, | ||
}, | ||
]; | ||
} | ||
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) | ||
] = (getHeader(req, 'content-type') || '') | ||
.replace(/\s/g, '') | ||
.toLowerCase() | ||
.split(';'); | ||
const partParams = {}; | ||
switch (true) { | ||
case method === 'GET': { | ||
// TODO: what if content-type is specified and is not application/x-www-form-urlencoded? | ||
try { | ||
const [, search] = req.url.split('?'); | ||
const searchParams = new URLSearchParams(search); | ||
partParams.operationName = | ||
(_a = searchParams.get('operationName')) !== null && _a !== void 0 ? _a : undefined; | ||
partParams.query = (_b = searchParams.get('query')) !== null && _b !== void 0 ? _b : undefined; | ||
const variables = searchParams.get('variables'); | ||
if (variables) | ||
partParams.variables = JSON.parse(variables); | ||
const extensions = searchParams.get('extensions'); | ||
if (extensions) | ||
partParams.extensions = JSON.parse(extensions); | ||
} | ||
catch (_c) { | ||
throw new Error('Unparsable URL'); | ||
} | ||
break; | ||
} | ||
case method === 'POST' && | ||
mediaType === 'application/json' && | ||
charset === 'charset=utf-8': | ||
{ | ||
if (!req.body) { | ||
throw new Error('Missing body'); | ||
} | ||
let data; | ||
try { | ||
const body = typeof req.body === 'function' ? await req.body() : req.body; | ||
data = typeof body === 'string' ? JSON.parse(body) : body; | ||
} | ||
catch (err) { | ||
throw new Error('Unparsable JSON body'); | ||
} | ||
if (!(0, utils_1.isObject)(data)) { | ||
throw new Error('JSON body must be an object'); | ||
} | ||
partParams.operationName = data.operationName; | ||
partParams.query = data.query; | ||
partParams.variables = data.variables; | ||
partParams.extensions = data.extensions; | ||
break; | ||
} | ||
default: // graphql-http doesnt support any other content type | ||
return [ | ||
null, | ||
{ | ||
status: 415, | ||
statusText: 'Unsupported Media Type', | ||
}, | ||
]; | ||
} | ||
if (partParams.query == null) | ||
throw new Error('Missing query'); | ||
if (typeof partParams.query !== 'string') | ||
throw new Error('Invalid query'); | ||
if (partParams.variables != null && | ||
(typeof partParams.variables !== 'object' || | ||
Array.isArray(partParams.variables))) { | ||
throw new Error('Invalid variables'); | ||
} | ||
if (partParams.operationName != null && | ||
typeof partParams.operationName !== 'string') { | ||
throw new Error('Invalid operationName'); | ||
} | ||
if (partParams.extensions != null && | ||
(typeof partParams.extensions !== 'object' || | ||
Array.isArray(partParams.extensions))) { | ||
throw new Error('Invalid extensions'); | ||
} | ||
// request parameters are checked and now complete | ||
return partParams; | ||
} | ||
exports.parseRequestParams = parseRequestParams; | ||
/** | ||
* Makes a GraphQL over HTTP spec compliant server handler. The handler can | ||
@@ -76,3 +187,3 @@ * be used with your favorite server library. | ||
function createHandler(options) { | ||
const { schema, context, validate = graphql_1.validate, validationRules = [], execute = graphql_1.execute, parse = graphql_1.parse, getOperationAST = graphql_1.getOperationAST, rootValue, onSubscribe, onOperation, formatError = (err) => err, parseRequestParams = defaultParseRequestParams, } = options; | ||
const { schema, context, validate = graphql_1.validate, validationRules = [], execute = graphql_1.execute, parse = graphql_1.parse, getOperationAST = graphql_1.getOperationAST, rootValue, onSubscribe, onOperation, formatError = (err) => err, parseRequestParams: optionsParseRequestParams = parseRequestParams, } = options; | ||
return async function handler(req) { | ||
@@ -117,5 +228,5 @@ let acceptedMediaType = null; | ||
try { | ||
let paramsOrRes = await parseRequestParams(req); | ||
let paramsOrRes = await optionsParseRequestParams(req); | ||
if (!paramsOrRes) | ||
paramsOrRes = await defaultParseRequestParams(req); | ||
paramsOrRes = await parseRequestParams(req); | ||
if (isResponse(paramsOrRes)) | ||
@@ -230,106 +341,2 @@ return paramsOrRes; | ||
/** | ||
* The default request params parser. Used when no custom one is provided or if it | ||
* returns nothing. | ||
* | ||
* Read more about it in {@link ParseRequestParams}. | ||
* | ||
* TODO: should graphql-http itself care about content-encoding? I'd say unzipping should happen before handler is reached | ||
*/ | ||
async function defaultParseRequestParams(req) { | ||
var _a, _b; | ||
const method = req.method; | ||
if (method !== 'GET' && method !== 'POST') { | ||
return [ | ||
null, | ||
{ | ||
status: 405, | ||
statusText: 'Method Not Allowed', | ||
headers: { | ||
allow: 'GET, POST', | ||
}, | ||
}, | ||
]; | ||
} | ||
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) | ||
] = (getHeader(req, 'content-type') || '') | ||
.replace(/\s/g, '') | ||
.toLowerCase() | ||
.split(';'); | ||
const partParams = {}; | ||
switch (true) { | ||
case method === 'GET': { | ||
// TODO: what if content-type is specified and is not application/x-www-form-urlencoded? | ||
try { | ||
const [, search] = req.url.split('?'); | ||
const searchParams = new URLSearchParams(search); | ||
partParams.operationName = | ||
(_a = searchParams.get('operationName')) !== null && _a !== void 0 ? _a : undefined; | ||
partParams.query = (_b = searchParams.get('query')) !== null && _b !== void 0 ? _b : undefined; | ||
const variables = searchParams.get('variables'); | ||
if (variables) | ||
partParams.variables = JSON.parse(variables); | ||
const extensions = searchParams.get('extensions'); | ||
if (extensions) | ||
partParams.extensions = JSON.parse(extensions); | ||
} | ||
catch (_c) { | ||
throw new Error('Unparsable URL'); | ||
} | ||
break; | ||
} | ||
case method === 'POST' && | ||
mediaType === 'application/json' && | ||
charset === 'charset=utf-8': | ||
{ | ||
if (!req.body) { | ||
throw new Error('Missing body'); | ||
} | ||
let data; | ||
try { | ||
const body = typeof req.body === 'function' ? await req.body() : req.body; | ||
data = typeof body === 'string' ? JSON.parse(body) : body; | ||
} | ||
catch (err) { | ||
throw new Error('Unparsable JSON body'); | ||
} | ||
if (!(0, utils_1.isObject)(data)) { | ||
throw new Error('JSON body must be an object'); | ||
} | ||
partParams.operationName = data.operationName; | ||
partParams.query = data.query; | ||
partParams.variables = data.variables; | ||
partParams.extensions = data.extensions; | ||
break; | ||
} | ||
default: // graphql-http doesnt support any other content type | ||
return [ | ||
null, | ||
{ | ||
status: 415, | ||
statusText: 'Unsupported Media Type', | ||
}, | ||
]; | ||
} | ||
if (partParams.query == null) | ||
throw new Error('Missing query'); | ||
if (typeof partParams.query !== 'string') | ||
throw new Error('Invalid query'); | ||
if (partParams.variables != null && | ||
(typeof partParams.variables !== 'object' || | ||
Array.isArray(partParams.variables))) { | ||
throw new Error('Invalid variables'); | ||
} | ||
if (partParams.operationName != null && | ||
typeof partParams.operationName !== 'string') { | ||
throw new Error('Invalid operationName'); | ||
} | ||
if (partParams.extensions != null && | ||
(typeof partParams.extensions !== 'object' || | ||
Array.isArray(partParams.extensions))) { | ||
throw new Error('Invalid extensions'); | ||
} | ||
// request parameters are checked and now complete | ||
return partParams; | ||
} | ||
/** | ||
* Creates an appropriate GraphQL over HTTP response following the provided arguments. | ||
@@ -336,0 +343,0 @@ * |
import type { Request, Response, Handler } from 'express'; | ||
import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler'; | ||
import { RequestParams } from '../common'; | ||
/** | ||
@@ -12,2 +13,42 @@ * The context in the request for the handler. | ||
/** | ||
* The GraphQL over HTTP spec compliant request parser for an incoming GraphQL request. | ||
* | ||
* 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 | ||
* on the `Response` argument and return `null`. | ||
* | ||
* 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, | ||
* the function will throw an error and it is up to the user to handle and respond as they see fit. | ||
* | ||
* ```js | ||
* import express from 'express'; // yarn add express | ||
* import { parseRequestParams } from 'graphql-http/lib/use/express'; | ||
* | ||
* const app = express(); | ||
* app.all('/graphql', async (req, res) => { | ||
* try { | ||
* const maybeParams = await parseRequestParams(req, res); | ||
* if (!maybeParams) { | ||
* // not a well-formatted GraphQL over HTTP request, | ||
* // parser responded and there's nothing else to do | ||
* return; | ||
* } | ||
* | ||
* // well-formatted GraphQL over HTTP request, | ||
* // with valid parameters | ||
* res.writeHead(200).end(JSON.stringify(maybeParams, null, ' ')); | ||
* } catch (err) { | ||
* // well-formatted GraphQL over HTTP request, | ||
* // but with invalid parameters | ||
* res.writeHead(400).end(err.message); | ||
* } | ||
* }); | ||
* | ||
* app.listen({ port: 4000 }); | ||
* console.log('Listening to port 4000'); | ||
* ``` | ||
* | ||
* @category Server/express | ||
*/ | ||
export declare function parseRequestParams(req: Request, res: Response): Promise<RequestParams | null>; | ||
/** | ||
* Handler options when using the express adapter. | ||
@@ -14,0 +55,0 @@ * |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.createHandler = void 0; | ||
exports.createHandler = exports.parseRequestParams = void 0; | ||
const handler_1 = require("../handler"); | ||
/** | ||
* The GraphQL over HTTP spec compliant request parser for an incoming GraphQL request. | ||
* | ||
* 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 | ||
* on the `Response` argument and return `null`. | ||
* | ||
* 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, | ||
* the function will throw an error and it is up to the user to handle and respond as they see fit. | ||
* | ||
* ```js | ||
* import express from 'express'; // yarn add express | ||
* import { parseRequestParams } from 'graphql-http/lib/use/express'; | ||
* | ||
* const app = express(); | ||
* app.all('/graphql', async (req, res) => { | ||
* try { | ||
* const maybeParams = await parseRequestParams(req, res); | ||
* if (!maybeParams) { | ||
* // not a well-formatted GraphQL over HTTP request, | ||
* // parser responded and there's nothing else to do | ||
* return; | ||
* } | ||
* | ||
* // well-formatted GraphQL over HTTP request, | ||
* // with valid parameters | ||
* res.writeHead(200).end(JSON.stringify(maybeParams, null, ' ')); | ||
* } catch (err) { | ||
* // well-formatted GraphQL over HTTP request, | ||
* // but with invalid parameters | ||
* res.writeHead(400).end(err.message); | ||
* } | ||
* }); | ||
* | ||
* app.listen({ port: 4000 }); | ||
* console.log('Listening to port 4000'); | ||
* ``` | ||
* | ||
* @category Server/express | ||
*/ | ||
async function parseRequestParams(req, res) { | ||
const rawReq = toRequest(req, res); | ||
const paramsOrRes = await (0, handler_1.parseRequestParams)(rawReq); | ||
if (!('query' in paramsOrRes)) { | ||
const [body, init] = paramsOrRes; | ||
res.writeHead(init.status, init.statusText, init.headers).end(body); | ||
return null; | ||
} | ||
return paramsOrRes; | ||
} | ||
exports.parseRequestParams = parseRequestParams; | ||
/** | ||
* Create a GraphQL over HTTP spec compliant request handler for | ||
@@ -27,20 +77,3 @@ * the express framework. | ||
try { | ||
const [body, init] = await handle({ | ||
url: req.url, | ||
method: req.method, | ||
headers: req.headers, | ||
body: () => { | ||
if (req.body) { | ||
// in case express has a body parser | ||
return req.body; | ||
} | ||
return new Promise((resolve) => { | ||
let body = ''; | ||
req.on('data', (chunk) => (body += chunk)); | ||
req.on('end', () => resolve(body)); | ||
}); | ||
}, | ||
raw: req, | ||
context: { res }, | ||
}); | ||
const [body, init] = await handle(toRequest(req, res)); | ||
res.writeHead(init.status, init.statusText, init.headers).end(body); | ||
@@ -58,1 +91,21 @@ } | ||
exports.createHandler = createHandler; | ||
function toRequest(req, res) { | ||
return { | ||
url: req.url, | ||
method: req.method, | ||
headers: req.headers, | ||
body: () => { | ||
if (req.body) { | ||
// in case express has a body parser | ||
return req.body; | ||
} | ||
return new Promise((resolve) => { | ||
let body = ''; | ||
req.on('data', (chunk) => (body += chunk)); | ||
req.on('end', () => resolve(body)); | ||
}); | ||
}, | ||
raw: req, | ||
context: { res }, | ||
}; | ||
} |
import type { FastifyRequest, FastifyReply, RouteHandler } from 'fastify'; | ||
import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler'; | ||
import { RequestParams } from '../common'; | ||
/** | ||
@@ -12,2 +13,42 @@ * The context in the request for the handler. | ||
/** | ||
* The GraphQL over HTTP spec compliant request parser for an incoming GraphQL request. | ||
* | ||
* 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 | ||
* on the `FastifyReply` argument and return `null`. | ||
* | ||
* 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, | ||
* the function will throw an error and it is up to the user to handle and respond as they see fit. | ||
* | ||
* ```js | ||
* import Fastify from 'fastify'; // yarn add fastify | ||
* import { parseRequestParams } from 'graphql-http/lib/use/fastify'; | ||
* | ||
* const fastify = Fastify(); | ||
* fastify.all('/graphql', async (req, reply) => { | ||
* try { | ||
* const maybeParams = await parseRequestParams(req, reply); | ||
* if (!maybeParams) { | ||
* // not a well-formatted GraphQL over HTTP request, | ||
* // parser responded and there's nothing else to do | ||
* return; | ||
* } | ||
* | ||
* // well-formatted GraphQL over HTTP request, | ||
* // with valid parameters | ||
* reply.status(200).send(JSON.stringify(maybeParams, null, ' ')); | ||
* } catch (err) { | ||
* // well-formatted GraphQL over HTTP request, | ||
* // but with invalid parameters | ||
* reply.status(400).send(err.message); | ||
* } | ||
* }); | ||
* | ||
* fastify.listen({ port: 4000 }); | ||
* console.log('Listening to port 4000'); | ||
* ``` | ||
* | ||
* @category Server/fastify | ||
*/ | ||
export declare function parseRequestParams(req: FastifyRequest, reply: FastifyReply): Promise<RequestParams | null>; | ||
/** | ||
* Handler options when using the fastify adapter. | ||
@@ -24,3 +65,3 @@ * | ||
* import Fastify from 'fastify'; // yarn add fastify | ||
* import { createHandler } from 'graphql-http/lib/use/express'; | ||
* import { createHandler } from 'graphql-http/lib/use/fastify'; | ||
* import { schema } from './my-graphql-schema'; | ||
@@ -27,0 +68,0 @@ * |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.createHandler = void 0; | ||
exports.createHandler = exports.parseRequestParams = void 0; | ||
const handler_1 = require("../handler"); | ||
/** | ||
* The GraphQL over HTTP spec compliant request parser for an incoming GraphQL request. | ||
* | ||
* 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 | ||
* on the `FastifyReply` argument and return `null`. | ||
* | ||
* 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, | ||
* the function will throw an error and it is up to the user to handle and respond as they see fit. | ||
* | ||
* ```js | ||
* import Fastify from 'fastify'; // yarn add fastify | ||
* import { parseRequestParams } from 'graphql-http/lib/use/fastify'; | ||
* | ||
* const fastify = Fastify(); | ||
* fastify.all('/graphql', async (req, reply) => { | ||
* try { | ||
* const maybeParams = await parseRequestParams(req, reply); | ||
* if (!maybeParams) { | ||
* // not a well-formatted GraphQL over HTTP request, | ||
* // parser responded and there's nothing else to do | ||
* return; | ||
* } | ||
* | ||
* // well-formatted GraphQL over HTTP request, | ||
* // with valid parameters | ||
* reply.status(200).send(JSON.stringify(maybeParams, null, ' ')); | ||
* } catch (err) { | ||
* // well-formatted GraphQL over HTTP request, | ||
* // but with invalid parameters | ||
* reply.status(400).send(err.message); | ||
* } | ||
* }); | ||
* | ||
* fastify.listen({ port: 4000 }); | ||
* console.log('Listening to port 4000'); | ||
* ``` | ||
* | ||
* @category Server/fastify | ||
*/ | ||
async function parseRequestParams(req, reply) { | ||
const rawReq = toRequest(req, reply); | ||
const paramsOrRes = await (0, handler_1.parseRequestParams)(rawReq); | ||
if (!('query' in paramsOrRes)) { | ||
const [body, init] = paramsOrRes; | ||
reply | ||
.status(init.status) | ||
.headers(init.headers || {}) | ||
// "or undefined" because `null` will be JSON stringified | ||
.send(body || undefined); | ||
return null; | ||
} | ||
return paramsOrRes; | ||
} | ||
exports.parseRequestParams = parseRequestParams; | ||
/** | ||
* Create a GraphQL over HTTP spec compliant request handler for | ||
@@ -11,3 +65,3 @@ * the fastify framework. | ||
* import Fastify from 'fastify'; // yarn add fastify | ||
* import { createHandler } from 'graphql-http/lib/use/express'; | ||
* import { createHandler } from 'graphql-http/lib/use/fastify'; | ||
* import { schema } from './my-graphql-schema'; | ||
@@ -28,11 +82,3 @@ * | ||
try { | ||
const [body, init] = await handle({ | ||
url: req.url, | ||
method: req.method, | ||
headers: req.headers, | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
body: req.body, | ||
raw: req, | ||
context: { reply }, | ||
}); | ||
const [body, init] = await handle(toRequest(req, reply)); | ||
reply | ||
@@ -54,1 +100,12 @@ .status(init.status) | ||
exports.createHandler = createHandler; | ||
function toRequest(req, reply) { | ||
return { | ||
url: req.url, | ||
method: req.method, | ||
headers: req.headers, | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
body: req.body, | ||
raw: req, | ||
context: { reply }, | ||
}; | ||
} |
import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler'; | ||
import { RequestParams } from '../common'; | ||
/** | ||
@@ -13,2 +14,51 @@ * The necessary API from the fetch environment for the handler. | ||
/** | ||
* The GraphQL over HTTP spec compliant request parser for an incoming GraphQL request. | ||
* | ||
* It is important to pass in the `abortedRef` so that the parser does not perform any | ||
* operations on a disposed request (see example). | ||
* | ||
* 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 return a `Response`. | ||
* | ||
* 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, | ||
* the function will throw an error and it is up to the user to handle and respond as they see fit. | ||
* | ||
* ```js | ||
* import http from 'http'; | ||
* import { createServerAdapter } from '@whatwg-node/server'; // yarn add @whatwg-node/server | ||
* import { parseRequestParams } from 'graphql-http/lib/use/fetch'; | ||
* | ||
* // Use this adapter in _any_ environment. | ||
* const adapter = createServerAdapter({ | ||
* handleRequest: async (req) => { | ||
* try { | ||
* const paramsOrResponse = await parseRequestParams(req); | ||
* if (paramsOrResponse instanceof Response) { | ||
* // not a well-formatted GraphQL over HTTP request, | ||
* // parser created a response object to use | ||
* return paramsOrResponse; | ||
* } | ||
* | ||
* // well-formatted GraphQL over HTTP request, | ||
* // with valid parameters | ||
* return new Response(JSON.stringify(paramsOrResponse, null, ' '), { | ||
* status: 200, | ||
* }); | ||
* } catch (err) { | ||
* // well-formatted GraphQL over HTTP request, | ||
* // but with invalid parameters | ||
* return new Response(err.message, { status: 400 }); | ||
* } | ||
* }, | ||
* }); | ||
* | ||
* const server = http.createServer(adapter); | ||
* | ||
* server.listen(4000); | ||
* console.log('Listening to port 4000'); | ||
* ``` | ||
* | ||
* @category Server/fetch | ||
*/ | ||
export declare function parseRequestParams(req: Request, api?: Partial<FetchAPI>): Promise<RequestParams | Response>; | ||
/** | ||
* Handler options when using the fetch adapter. | ||
@@ -15,0 +65,0 @@ * |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.createHandler = void 0; | ||
exports.createHandler = exports.parseRequestParams = void 0; | ||
const handler_1 = require("../handler"); | ||
/** | ||
* The GraphQL over HTTP spec compliant request parser for an incoming GraphQL request. | ||
* | ||
* It is important to pass in the `abortedRef` so that the parser does not perform any | ||
* operations on a disposed request (see example). | ||
* | ||
* 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 return a `Response`. | ||
* | ||
* 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, | ||
* the function will throw an error and it is up to the user to handle and respond as they see fit. | ||
* | ||
* ```js | ||
* import http from 'http'; | ||
* import { createServerAdapter } from '@whatwg-node/server'; // yarn add @whatwg-node/server | ||
* import { parseRequestParams } from 'graphql-http/lib/use/fetch'; | ||
* | ||
* // Use this adapter in _any_ environment. | ||
* const adapter = createServerAdapter({ | ||
* handleRequest: async (req) => { | ||
* try { | ||
* const paramsOrResponse = await parseRequestParams(req); | ||
* if (paramsOrResponse instanceof Response) { | ||
* // not a well-formatted GraphQL over HTTP request, | ||
* // parser created a response object to use | ||
* return paramsOrResponse; | ||
* } | ||
* | ||
* // well-formatted GraphQL over HTTP request, | ||
* // with valid parameters | ||
* return new Response(JSON.stringify(paramsOrResponse, null, ' '), { | ||
* status: 200, | ||
* }); | ||
* } catch (err) { | ||
* // well-formatted GraphQL over HTTP request, | ||
* // but with invalid parameters | ||
* return new Response(err.message, { status: 400 }); | ||
* } | ||
* }, | ||
* }); | ||
* | ||
* const server = http.createServer(adapter); | ||
* | ||
* server.listen(4000); | ||
* console.log('Listening to port 4000'); | ||
* ``` | ||
* | ||
* @category Server/fetch | ||
*/ | ||
async function parseRequestParams(req, api = {}) { | ||
const rawReq = toRequest(req, api); | ||
const paramsOrRes = await (0, handler_1.parseRequestParams)(rawReq); | ||
if (!('query' in paramsOrRes)) { | ||
const [body, init] = paramsOrRes; | ||
return new (api.Response || Response)(body, init); | ||
} | ||
return paramsOrRes; | ||
} | ||
exports.parseRequestParams = parseRequestParams; | ||
/** | ||
* Create a GraphQL over HTTP spec compliant request handler for | ||
@@ -42,10 +100,3 @@ * a fetch environment like Deno, Bun, CloudFlare Workers, Lambdas, etc. | ||
try { | ||
const [body, init] = await handler({ | ||
method: req.method, | ||
url: req.url, | ||
headers: req.headers, | ||
body: () => req.text(), | ||
raw: req, | ||
context: api, | ||
}); | ||
const [body, init] = await handler(toRequest(req, api)); | ||
return new api.Response(body, init); | ||
@@ -63,1 +114,15 @@ } | ||
exports.createHandler = createHandler; | ||
function toRequest(req, api = {}) { | ||
return { | ||
method: req.method, | ||
url: req.url, | ||
headers: req.headers, | ||
body: () => req.text(), | ||
raw: req, | ||
context: { | ||
Response: api.Response || Response, | ||
TextEncoder: api.TextEncoder || TextEncoder, | ||
ReadableStream: api.ReadableStream || ReadableStream, | ||
}, | ||
}; | ||
} |
/// <reference types="node" /> | ||
import type { IncomingMessage, ServerResponse } from 'http'; | ||
import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler'; | ||
import { RequestParams } from '../common'; | ||
/** | ||
@@ -13,2 +14,45 @@ * The context in the request for the handler. | ||
/** | ||
* The GraphQL over HTTP spec compliant request parser for an incoming GraphQL request. | ||
* | ||
* 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 | ||
* on the `ServerResponse` argument and return `null`. | ||
* | ||
* 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, | ||
* the function will throw an error and it is up to the user to handle and respond as they see fit. | ||
* | ||
* ```js | ||
* import http from 'http'; | ||
* import { parseRequestParams } from 'graphql-http/lib/use/http'; | ||
* | ||
* const server = http.createServer(async (req, res) => { | ||
* if (req.url.startsWith('/graphql')) { | ||
* try { | ||
* const maybeParams = await parseRequestParams(req, res); | ||
* if (!maybeParams) { | ||
* // not a well-formatted GraphQL over HTTP request, | ||
* // parser responded and there's nothing else to do | ||
* return; | ||
* } | ||
* | ||
* // well-formatted GraphQL over HTTP request, | ||
* // with valid parameters | ||
* res.writeHead(200).end(JSON.stringify(maybeParams, null, ' ')); | ||
* } catch (err) { | ||
* // well-formatted GraphQL over HTTP request, | ||
* // but with invalid parameters | ||
* res.writeHead(400).end(err.message); | ||
* } | ||
* } else { | ||
* res.writeHead(404).end(); | ||
* } | ||
* }); | ||
* | ||
* server.listen(4000); | ||
* console.log('Listening to port 4000'); | ||
* ``` | ||
* | ||
* @category Server/http | ||
*/ | ||
export declare function parseRequestParams(req: IncomingMessage, res: ServerResponse): Promise<RequestParams | null>; | ||
/** | ||
* Handler options when using the http adapter. | ||
@@ -15,0 +59,0 @@ * |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.createHandler = void 0; | ||
exports.createHandler = exports.parseRequestParams = void 0; | ||
const handler_1 = require("../handler"); | ||
/** | ||
* The GraphQL over HTTP spec compliant request parser for an incoming GraphQL request. | ||
* | ||
* 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 | ||
* on the `ServerResponse` argument and return `null`. | ||
* | ||
* 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, | ||
* the function will throw an error and it is up to the user to handle and respond as they see fit. | ||
* | ||
* ```js | ||
* import http from 'http'; | ||
* import { parseRequestParams } from 'graphql-http/lib/use/http'; | ||
* | ||
* const server = http.createServer(async (req, res) => { | ||
* if (req.url.startsWith('/graphql')) { | ||
* try { | ||
* const maybeParams = await parseRequestParams(req, res); | ||
* if (!maybeParams) { | ||
* // not a well-formatted GraphQL over HTTP request, | ||
* // parser responded and there's nothing else to do | ||
* return; | ||
* } | ||
* | ||
* // well-formatted GraphQL over HTTP request, | ||
* // with valid parameters | ||
* res.writeHead(200).end(JSON.stringify(maybeParams, null, ' ')); | ||
* } catch (err) { | ||
* // well-formatted GraphQL over HTTP request, | ||
* // but with invalid parameters | ||
* res.writeHead(400).end(err.message); | ||
* } | ||
* } else { | ||
* res.writeHead(404).end(); | ||
* } | ||
* }); | ||
* | ||
* server.listen(4000); | ||
* console.log('Listening to port 4000'); | ||
* ``` | ||
* | ||
* @category Server/http | ||
*/ | ||
async function parseRequestParams(req, res) { | ||
const rawReq = toRequest(req, res); | ||
const paramsOrRes = await (0, handler_1.parseRequestParams)(rawReq); | ||
if (!('query' in paramsOrRes)) { | ||
const [body, init] = paramsOrRes; | ||
res.writeHead(init.status, init.statusText, init.headers).end(body); | ||
return null; | ||
} | ||
return paramsOrRes; | ||
} | ||
exports.parseRequestParams = parseRequestParams; | ||
/** | ||
* Create a GraphQL over HTTP spec compliant request handler for | ||
@@ -32,14 +85,3 @@ * the Node environment http module. | ||
} | ||
const [body, init] = await handle({ | ||
url: req.url, | ||
method: req.method, | ||
headers: req.headers, | ||
body: () => new Promise((resolve) => { | ||
let body = ''; | ||
req.on('data', (chunk) => (body += chunk)); | ||
req.on('end', () => resolve(body)); | ||
}), | ||
raw: req, | ||
context: { res }, | ||
}); | ||
const [body, init] = await handle(toRequest(req, res)); | ||
res.writeHead(init.status, init.statusText, init.headers).end(body); | ||
@@ -57,1 +99,21 @@ } | ||
exports.createHandler = createHandler; | ||
function toRequest(req, res) { | ||
if (!req.url) { | ||
throw new Error('Missing request URL'); | ||
} | ||
if (!req.method) { | ||
throw new Error('Missing request method'); | ||
} | ||
return { | ||
url: req.url, | ||
method: req.method, | ||
headers: req.headers, | ||
body: () => new Promise((resolve) => { | ||
let body = ''; | ||
req.on('data', (chunk) => (body += chunk)); | ||
req.on('end', () => resolve(body)); | ||
}), | ||
raw: req, | ||
context: { res }, | ||
}; | ||
} |
/// <reference types="node" /> | ||
import type { Http2ServerRequest, Http2ServerResponse } from 'http2'; | ||
import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler'; | ||
import { RequestParams } from '../common'; | ||
/** | ||
@@ -13,2 +14,57 @@ * The context in the request for the handler. | ||
/** | ||
* The GraphQL over HTTP spec compliant request parser for an incoming GraphQL request. | ||
* | ||
* 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 | ||
* on the `Http2ServerResponse` argument and return `null`. | ||
* | ||
* 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, | ||
* the function will throw an error and it is up to the user to handle and respond as they see fit. | ||
* | ||
* ```shell | ||
* $ openssl req -x509 -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' \ | ||
* -keyout localhost-privkey.pem -out localhost-cert.pem | ||
* ``` | ||
* | ||
* ```js | ||
* import fs from 'fs'; | ||
* import http2 from 'http2'; | ||
* import { parseRequestParams } from 'graphql-http/lib/use/http2'; | ||
* | ||
* const server = http2.createSecureServer( | ||
* { | ||
* key: fs.readFileSync('localhost-privkey.pem'), | ||
* cert: fs.readFileSync('localhost-cert.pem'), | ||
* }, | ||
* async (req, res) => { | ||
* if (req.url.startsWith('/graphql')) { | ||
* try { | ||
* const maybeParams = await parseRequestParams(req, res); | ||
* if (!maybeParams) { | ||
* // not a well-formatted GraphQL over HTTP request, | ||
* // parser responded and there's nothing else to do | ||
* return; | ||
* } | ||
* | ||
* // well-formatted GraphQL over HTTP request, | ||
* // with valid parameters | ||
* res.writeHead(200).end(JSON.stringify(maybeParams, null, ' ')); | ||
* } catch (err) { | ||
* // well-formatted GraphQL over HTTP request, | ||
* // but with invalid parameters | ||
* res.writeHead(400).end(err.message); | ||
* } | ||
* } else { | ||
* res.writeHead(404).end(); | ||
* } | ||
* }, | ||
* ); | ||
* | ||
* server.listen(4000); | ||
* console.log('Listening to port 4000'); | ||
* ``` | ||
* | ||
* @category Server/http2 | ||
*/ | ||
export declare function parseRequestParams(req: Http2ServerRequest, res: Http2ServerResponse): Promise<RequestParams | null>; | ||
/** | ||
* Handler options when using the http adapter. | ||
@@ -15,0 +71,0 @@ * |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.createHandler = void 0; | ||
exports.createHandler = exports.parseRequestParams = void 0; | ||
const handler_1 = require("../handler"); | ||
/** | ||
* The GraphQL over HTTP spec compliant request parser for an incoming GraphQL request. | ||
* | ||
* 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 | ||
* on the `Http2ServerResponse` argument and return `null`. | ||
* | ||
* 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, | ||
* the function will throw an error and it is up to the user to handle and respond as they see fit. | ||
* | ||
* ```shell | ||
* $ openssl req -x509 -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' \ | ||
* -keyout localhost-privkey.pem -out localhost-cert.pem | ||
* ``` | ||
* | ||
* ```js | ||
* import fs from 'fs'; | ||
* import http2 from 'http2'; | ||
* import { parseRequestParams } from 'graphql-http/lib/use/http2'; | ||
* | ||
* const server = http2.createSecureServer( | ||
* { | ||
* key: fs.readFileSync('localhost-privkey.pem'), | ||
* cert: fs.readFileSync('localhost-cert.pem'), | ||
* }, | ||
* async (req, res) => { | ||
* if (req.url.startsWith('/graphql')) { | ||
* try { | ||
* const maybeParams = await parseRequestParams(req, res); | ||
* if (!maybeParams) { | ||
* // not a well-formatted GraphQL over HTTP request, | ||
* // parser responded and there's nothing else to do | ||
* return; | ||
* } | ||
* | ||
* // well-formatted GraphQL over HTTP request, | ||
* // with valid parameters | ||
* res.writeHead(200).end(JSON.stringify(maybeParams, null, ' ')); | ||
* } catch (err) { | ||
* // well-formatted GraphQL over HTTP request, | ||
* // but with invalid parameters | ||
* res.writeHead(400).end(err.message); | ||
* } | ||
* } else { | ||
* res.writeHead(404).end(); | ||
* } | ||
* }, | ||
* ); | ||
* | ||
* server.listen(4000); | ||
* console.log('Listening to port 4000'); | ||
* ``` | ||
* | ||
* @category Server/http2 | ||
*/ | ||
async function parseRequestParams(req, res) { | ||
const rawReq = toRequest(req, res); | ||
const paramsOrRes = await (0, handler_1.parseRequestParams)(rawReq); | ||
if (!('query' in paramsOrRes)) { | ||
const [body, init] = paramsOrRes; | ||
res.writeHead(init.status, init.statusText, init.headers); | ||
if (body) { | ||
res.end(body); | ||
} | ||
else { | ||
res.end(); | ||
} | ||
return null; | ||
} | ||
return paramsOrRes; | ||
} | ||
exports.parseRequestParams = parseRequestParams; | ||
/** | ||
* Create a GraphQL over HTTP spec compliant request handler for | ||
@@ -44,14 +115,3 @@ * the Node environment http2 module. | ||
} | ||
const [body, init] = await handle({ | ||
url: req.url, | ||
method: req.method, | ||
headers: req.headers, | ||
body: () => new Promise((resolve) => { | ||
let body = ''; | ||
req.on('data', (chunk) => (body += chunk)); | ||
req.on('end', () => resolve(body)); | ||
}), | ||
raw: req, | ||
context: { res }, | ||
}); | ||
const [body, init] = await handle(toRequest(req, res)); | ||
res.writeHead(init.status, init.statusText, init.headers); | ||
@@ -75,1 +135,21 @@ if (body) { | ||
exports.createHandler = createHandler; | ||
function toRequest(req, res) { | ||
if (!req.url) { | ||
throw new Error('Missing request URL'); | ||
} | ||
if (!req.method) { | ||
throw new Error('Missing request method'); | ||
} | ||
return { | ||
url: req.url, | ||
method: req.method, | ||
headers: req.headers, | ||
body: () => new Promise((resolve) => { | ||
let body = ''; | ||
req.on('data', (chunk) => (body += chunk)); | ||
req.on('end', () => resolve(body)); | ||
}), | ||
raw: req, | ||
context: { res }, | ||
}; | ||
} |
/// <reference types="node" /> | ||
import type { Middleware, Response } from 'koa'; | ||
import type { Middleware, ParameterizedContext, Response } from 'koa'; | ||
import type { IncomingMessage } from 'http'; | ||
import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler'; | ||
import { RequestParams } from '../common'; | ||
/** | ||
@@ -14,2 +15,47 @@ * The context in the request for the handler. | ||
/** | ||
* The GraphQL over HTTP spec compliant request parser for an incoming GraphQL request. | ||
* | ||
* 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 | ||
* on Koa's `ParameterizedContext` response and return `null`. | ||
* | ||
* 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, | ||
* the function will throw an error and it is up to the user to handle and respond as they see fit. | ||
* | ||
* ```js | ||
* import Koa from 'koa'; // yarn add koa | ||
* import mount from 'koa-mount'; // yarn add koa-mount | ||
* import { parseRequestParams } from 'graphql-http/lib/use/koa'; | ||
* | ||
* const app = new Koa(); | ||
* app.use( | ||
* mount('/', async (ctx) => { | ||
* try { | ||
* const maybeParams = await parseRequestParams(ctx); | ||
* if (!maybeParams) { | ||
* // not a well-formatted GraphQL over HTTP request, | ||
* // parser responded and there's nothing else to do | ||
* return; | ||
* } | ||
* | ||
* // well-formatted GraphQL over HTTP request, | ||
* // with valid parameters | ||
* ctx.response.status = 200; | ||
* ctx.body = JSON.stringify(maybeParams, null, ' '); | ||
* } catch (err) { | ||
* // well-formatted GraphQL over HTTP request, | ||
* // but with invalid parameters | ||
* ctx.response.status = 400; | ||
* ctx.body = err.message; | ||
* } | ||
* }), | ||
* ); | ||
* | ||
* app.listen({ port: 4000 }); | ||
* console.log('Listening to port 4000'); | ||
* ``` | ||
* | ||
* @category Server/koa | ||
*/ | ||
export declare function parseRequestParams(ctx: ParameterizedContext): Promise<RequestParams | null>; | ||
/** | ||
* Handler options when using the koa adapter. | ||
@@ -16,0 +62,0 @@ * |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.createHandler = void 0; | ||
exports.createHandler = exports.parseRequestParams = void 0; | ||
const handler_1 = require("../handler"); | ||
/** | ||
* The GraphQL over HTTP spec compliant request parser for an incoming GraphQL request. | ||
* | ||
* 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 | ||
* on Koa's `ParameterizedContext` response and return `null`. | ||
* | ||
* 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, | ||
* the function will throw an error and it is up to the user to handle and respond as they see fit. | ||
* | ||
* ```js | ||
* import Koa from 'koa'; // yarn add koa | ||
* import mount from 'koa-mount'; // yarn add koa-mount | ||
* import { parseRequestParams } from 'graphql-http/lib/use/koa'; | ||
* | ||
* const app = new Koa(); | ||
* app.use( | ||
* mount('/', async (ctx) => { | ||
* try { | ||
* const maybeParams = await parseRequestParams(ctx); | ||
* if (!maybeParams) { | ||
* // not a well-formatted GraphQL over HTTP request, | ||
* // parser responded and there's nothing else to do | ||
* return; | ||
* } | ||
* | ||
* // well-formatted GraphQL over HTTP request, | ||
* // with valid parameters | ||
* ctx.response.status = 200; | ||
* ctx.body = JSON.stringify(maybeParams, null, ' '); | ||
* } catch (err) { | ||
* // well-formatted GraphQL over HTTP request, | ||
* // but with invalid parameters | ||
* ctx.response.status = 400; | ||
* ctx.body = err.message; | ||
* } | ||
* }), | ||
* ); | ||
* | ||
* app.listen({ port: 4000 }); | ||
* console.log('Listening to port 4000'); | ||
* ``` | ||
* | ||
* @category Server/koa | ||
*/ | ||
async function parseRequestParams(ctx) { | ||
const rawReq = toRequest(ctx); | ||
const paramsOrRes = await (0, handler_1.parseRequestParams)(rawReq); | ||
if (!('query' in paramsOrRes)) { | ||
const [body, init] = paramsOrRes; | ||
ctx.body = body; | ||
ctx.response.status = init.status; | ||
ctx.response.message = init.statusText; | ||
if (init.headers) { | ||
for (const [name, value] of Object.entries(init.headers)) { | ||
ctx.response.set(name, value); | ||
} | ||
} | ||
return null; | ||
} | ||
return paramsOrRes; | ||
} | ||
exports.parseRequestParams = parseRequestParams; | ||
/** | ||
* Create a GraphQL over HTTP spec compliant request handler for | ||
@@ -65,1 +127,22 @@ * the Koa framework. | ||
exports.createHandler = createHandler; | ||
function toRequest(ctx) { | ||
return { | ||
url: ctx.url, | ||
method: ctx.method, | ||
headers: ctx.headers, | ||
body: () => { | ||
if (ctx.body) { | ||
// in case koa has a body parser | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
return ctx.body; | ||
} | ||
return new Promise((resolve) => { | ||
let body = ''; | ||
ctx.req.on('data', (chunk) => (body += chunk)); | ||
ctx.req.on('end', () => resolve(body)); | ||
}); | ||
}, | ||
raw: ctx.req, | ||
context: { res: ctx.response }, | ||
}; | ||
} |
import type { HttpRequest, HttpResponse } from 'uWebSockets.js'; | ||
import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler'; | ||
import { RequestParams } from '../common'; | ||
/** | ||
@@ -12,2 +13,56 @@ * The context in the request for the handler. | ||
/** | ||
* The GraphQL over HTTP spec compliant request parser for an incoming GraphQL request. | ||
* | ||
* It is important to pass in the `abortedRef` so that the parser does not perform any | ||
* operations on a disposed request (see example). | ||
* | ||
* 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 | ||
* on the `HttpResponse` argument and return `null`. | ||
* | ||
* 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, | ||
* the function will throw an error and it is up to the user to handle and respond as they see fit. | ||
* | ||
* ```js | ||
* import uWS from 'uWebSockets.js'; // yarn add uWebSockets.js@uNetworking/uWebSockets.js#<version> | ||
* import { parseRequestParams } from 'graphql-http/lib/use/uWebSockets'; | ||
* | ||
* uWS | ||
* .App() | ||
* .any('/graphql', async (res, req) => { | ||
* const abortedRef = { current: false }; | ||
* res.onAborted(() => (abortedRef.current = true)); | ||
* try { | ||
* const maybeParams = await parseRequestParams(req, res, abortedRef); | ||
* if (!maybeParams) { | ||
* // not a well-formatted GraphQL over HTTP request, | ||
* // parser responded and there's nothing else to do | ||
* return; | ||
* } | ||
* | ||
* // well-formatted GraphQL over HTTP request, | ||
* // with valid parameters | ||
* if (!abortedRef.current) { | ||
* res.writeStatus('200 OK'); | ||
* res.end(JSON.stringify(maybeParams, null, ' ')); | ||
* } | ||
* } catch (err) { | ||
* // well-formatted GraphQL over HTTP request, | ||
* // but with invalid parameters | ||
* if (!abortedRef.current) { | ||
* res.writeStatus('400 Bad Request'); | ||
* res.end(err.message); | ||
* } | ||
* } | ||
* }) | ||
* .listen(4000, () => { | ||
* console.log('Listening to port 4000'); | ||
* }); | ||
* ``` | ||
* | ||
* @category Server/uWebSockets | ||
*/ | ||
export declare function parseRequestParams(req: HttpRequest, res: HttpResponse, abortedRef: { | ||
current: boolean; | ||
}): Promise<RequestParams | null>; | ||
/** | ||
* Handler options when using the http adapter. | ||
@@ -14,0 +69,0 @@ * |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.createHandler = void 0; | ||
exports.createHandler = exports.parseRequestParams = void 0; | ||
const handler_1 = require("../handler"); | ||
/** | ||
* The GraphQL over HTTP spec compliant request parser for an incoming GraphQL request. | ||
* | ||
* It is important to pass in the `abortedRef` so that the parser does not perform any | ||
* operations on a disposed request (see example). | ||
* | ||
* 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 | ||
* on the `HttpResponse` argument and return `null`. | ||
* | ||
* 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, | ||
* the function will throw an error and it is up to the user to handle and respond as they see fit. | ||
* | ||
* ```js | ||
* import uWS from 'uWebSockets.js'; // yarn add uWebSockets.js@uNetworking/uWebSockets.js#<version> | ||
* import { parseRequestParams } from 'graphql-http/lib/use/uWebSockets'; | ||
* | ||
* uWS | ||
* .App() | ||
* .any('/graphql', async (res, req) => { | ||
* const abortedRef = { current: false }; | ||
* res.onAborted(() => (abortedRef.current = true)); | ||
* try { | ||
* const maybeParams = await parseRequestParams(req, res, abortedRef); | ||
* if (!maybeParams) { | ||
* // not a well-formatted GraphQL over HTTP request, | ||
* // parser responded and there's nothing else to do | ||
* return; | ||
* } | ||
* | ||
* // well-formatted GraphQL over HTTP request, | ||
* // with valid parameters | ||
* if (!abortedRef.current) { | ||
* res.writeStatus('200 OK'); | ||
* res.end(JSON.stringify(maybeParams, null, ' ')); | ||
* } | ||
* } catch (err) { | ||
* // well-formatted GraphQL over HTTP request, | ||
* // but with invalid parameters | ||
* if (!abortedRef.current) { | ||
* res.writeStatus('400 Bad Request'); | ||
* res.end(err.message); | ||
* } | ||
* } | ||
* }) | ||
* .listen(4000, () => { | ||
* console.log('Listening to port 4000'); | ||
* }); | ||
* ``` | ||
* | ||
* @category Server/uWebSockets | ||
*/ | ||
async function parseRequestParams(req, res, abortedRef) { | ||
const rawReq = toRequest(req, res, abortedRef); | ||
const paramsOrRes = await (0, handler_1.parseRequestParams)(rawReq); | ||
if (!('query' in paramsOrRes)) { | ||
if (!abortedRef.current) { | ||
const [body, init] = paramsOrRes; | ||
res.cork(() => { | ||
res.writeStatus(`${init.status} ${init.statusText}`); | ||
for (const [key, val] of Object.entries(init.headers || {})) { | ||
res.writeHeader(key, val); | ||
} | ||
if (body) { | ||
res.end(body); | ||
} | ||
else { | ||
res.endWithoutBody(); | ||
} | ||
}); | ||
} | ||
return null; | ||
} | ||
return paramsOrRes; | ||
} | ||
exports.parseRequestParams = parseRequestParams; | ||
/** | ||
* Create a GraphQL over HTTP spec compliant request handler for | ||
@@ -27,32 +102,7 @@ * the Node environment [uWebSockets.js module](https://github.com/uNetworking/uWebSockets.js/). | ||
return async function requestListener(res, req) { | ||
let aborted = false; | ||
res.onAborted(() => (aborted = true)); | ||
const abortedRef = { current: false }; | ||
res.onAborted(() => (abortedRef.current = true)); | ||
try { | ||
let url = req.getUrl(); | ||
const query = req.getQuery(); | ||
if (query) { | ||
url += '?' + query; | ||
} | ||
const [body, init] = await handle({ | ||
url, | ||
method: req.getMethod().toUpperCase(), | ||
headers: { get: (key) => req.getHeader(key) }, | ||
body: () => new Promise((resolve) => { | ||
let body = ''; | ||
if (aborted) { | ||
resolve(body); | ||
} | ||
else { | ||
res.onData((chunk, isLast) => { | ||
body += Buffer.from(chunk, 0, chunk.byteLength).toString(); | ||
if (isLast) { | ||
resolve(body); | ||
} | ||
}); | ||
} | ||
}), | ||
raw: req, | ||
context: { res }, | ||
}); | ||
if (!aborted) { | ||
const [body, init] = await handle(toRequest(req, res, abortedRef)); | ||
if (!abortedRef.current) { | ||
res.cork(() => { | ||
@@ -77,3 +127,3 @@ res.writeStatus(`${init.status} ${init.statusText}`); | ||
'Please check your implementation.', err); | ||
if (!aborted) { | ||
if (!abortedRef.current) { | ||
res.cork(() => { | ||
@@ -87,1 +137,29 @@ res.writeStatus('500 Internal Server Error').endWithoutBody(); | ||
exports.createHandler = createHandler; | ||
function toRequest(req, res, abortedRef) { | ||
let url = req.getUrl(); | ||
const query = req.getQuery(); | ||
if (query) { | ||
url += '?' + query; | ||
} | ||
return { | ||
url, | ||
method: req.getMethod().toUpperCase(), | ||
headers: { get: (key) => req.getHeader(key) }, | ||
body: () => new Promise((resolve) => { | ||
let body = ''; | ||
if (abortedRef.current) { | ||
resolve(body); | ||
} | ||
else { | ||
res.onData((chunk, isLast) => { | ||
body += Buffer.from(chunk, 0, chunk.byteLength).toString(); | ||
if (isLast) { | ||
resolve(body); | ||
} | ||
}); | ||
} | ||
}), | ||
raw: req, | ||
context: { res }, | ||
}; | ||
} |
{ | ||
"name": "graphql-http", | ||
"version": "1.21.0", | ||
"version": "1.22.0", | ||
"description": "Simple, pluggable, zero-dependency, GraphQL over HTTP spec compliant server, client and audit suite.", | ||
@@ -119,3 +119,3 @@ "keywords": [ | ||
"devDependencies": { | ||
"@cspell/cspell-types": "^6.31.1", | ||
"@cspell/cspell-types": "^7.0.0", | ||
"@netlify/functions": "^1.6.0", | ||
@@ -126,35 +126,35 @@ "@rollup/plugin-terser": "^0.4.3", | ||
"@semantic-release/git": "^10.0.1", | ||
"@types/eslint": "^8.44.0", | ||
"@types/eslint": "^8.44.2", | ||
"@types/express": "^4.17.17", | ||
"@types/glob": "^8.1.0", | ||
"@types/html-validator": "^5.0.3", | ||
"@types/k6": "^0.45.0", | ||
"@types/koa": "^2.13.6", | ||
"@types/k6": "^0.46.0", | ||
"@types/koa": "^2.13.8", | ||
"@types/koa-mount": "^4.0.2", | ||
"@typescript-eslint/eslint-plugin": "^5.61.0", | ||
"@typescript-eslint/parser": "^5.61.0", | ||
"@whatwg-node/fetch": "^0.9.7", | ||
"cspell": "^6.31.1", | ||
"eslint": "^8.44.0", | ||
"eslint-config-prettier": "^8.8.0", | ||
"@typescript-eslint/eslint-plugin": "^6.4.0", | ||
"@typescript-eslint/parser": "^6.4.0", | ||
"@whatwg-node/fetch": "^0.9.9", | ||
"cspell": "^7.0.0", | ||
"eslint": "^8.47.0", | ||
"eslint-config-prettier": "^9.0.0", | ||
"express": "^4.18.2", | ||
"fastify": "^4.19.2", | ||
"glob": "^10.3.2", | ||
"graphql": "^16.7.1", | ||
"fastify": "^4.21.0", | ||
"glob": "^10.3.3", | ||
"graphql": "^16.8.0", | ||
"html-validator": "^6.0.1", | ||
"koa": "^2.14.2", | ||
"koa-mount": "^4.0.0", | ||
"node-fetch": "^3.3.1", | ||
"prettier": "^2.8.8", | ||
"rollup": "^3.26.2", | ||
"node-fetch": "^3.3.2", | ||
"prettier": "^3.0.1", | ||
"rollup": "^3.28.0", | ||
"rollup-plugin-gzip": "^3.1.0", | ||
"semantic-release": "^21.0.7", | ||
"tslib": "^2.6.0", | ||
"tslib": "^2.6.1", | ||
"tsx": "^3.12.7", | ||
"typedoc": "^0.24.8", | ||
"typedoc-plugin-markdown": "^3.15.3", | ||
"typedoc-plugin-markdown": "^3.15.4", | ||
"typescript": "^5.1.6", | ||
"uWebSockets.js": "uNetworking/uWebSockets.js#v20.30.0", | ||
"vitest": "^0.33.0" | ||
"uWebSockets.js": "uNetworking/uWebSockets.js#v20.31.0", | ||
"vitest": "^0.34.1" | ||
} | ||
} |
@@ -0,1 +1,3 @@ | ||
[![GraphQL Conf 2023](/GraphQLConf-2023-Banner.png)](https://graphql.org/conf/) | ||
<div align="center"> | ||
@@ -500,3 +502,3 @@ <br /> | ||
```html | ||
<!DOCTYPE html> | ||
<!doctype html> | ||
<html> | ||
@@ -503,0 +505,0 @@ <head> |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
403943
7648
911