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

Commit 11719a3

Browse files
authored
Merge pull request #150 from graphql/export-get-params
Adds getGraphQLParams
2 parents 4c7ffcf + d9c4119 commit 11719a3

File tree

3 files changed

+58
-26
lines changed

3 files changed

+58
-26
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,15 @@ new GraphQLObjectType({
141141
```
142142

143143

144+
## Other Exports
145+
146+
**`getGraphQLParams(request: Request): Promise<GraphQLParams>`**
147+
148+
Given an HTTP Request, this returns a Promise for the parameters relevant to
149+
running a GraphQL request. This function is used internally to handle the
150+
incoming request, you may use it directly for building other similar services.
151+
152+
144153
## Debugging Tips
145154

146155
During development, it's useful to get more information from errors, such as

src/index.js

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,15 @@ import url from 'url';
2424
import { parseBody } from './parseBody';
2525
import { renderGraphiQL } from './renderGraphiQL';
2626

27-
import type { Request, Response } from 'express';
28-
27+
import type { Response } from 'express';
28+
29+
export type Request = {
30+
method: string;
31+
url: string;
32+
body: mixed;
33+
headers: {[header: string]: mixed};
34+
pipe<T>(stream: T): T;
35+
};
2936

3037
/**
3138
* Used to configure the graphqlHTTP middleware by providing a schema
@@ -147,17 +154,14 @@ export default function graphqlHTTP(options: Options): Middleware {
147154
throw httpError(405, 'GraphQL only supports GET and POST requests.');
148155
}
149156

150-
// Parse the Request body.
151-
return parseBody(request);
152-
}).then(bodyData => {
153-
const urlData = request.url && url.parse(request.url, true).query || {};
154-
showGraphiQL = graphiql && canDisplayGraphiQL(request, urlData, bodyData);
155-
157+
// Parse the Request to get GraphQL request parameters.
158+
return getGraphQLParams(request);
159+
}).then(params => {
156160
// Get GraphQL params from the request and POST body data.
157-
const params = getGraphQLParams(urlData, bodyData);
158161
query = params.query;
159162
variables = params.variables;
160163
operationName = params.operationName;
164+
showGraphiQL = graphiql && canDisplayGraphiQL(request, params);
161165

162166
// If there is no query, but GraphiQL will be displayed, do not produce
163167
// a result, otherwise return a 400: Bad Request.
@@ -252,16 +256,28 @@ export default function graphqlHTTP(options: Options): Middleware {
252256
};
253257
}
254258

255-
type GraphQLParams = {
259+
export type GraphQLParams = {
256260
query: ?string;
257261
variables: ?{[name: string]: mixed};
258262
operationName: ?string;
263+
raw: ?boolean;
259264
};
260265

266+
/**
267+
* Provided a "Request" provided by express or connect (typically a node style
268+
* HTTPClientRequest), Promise the GraphQL request parameters.
269+
*/
270+
export function getGraphQLParams(request: Request): Promise<GraphQLParams> {
271+
return parseBody(request).then(bodyData => {
272+
const urlData = request.url && url.parse(request.url, true).query || {};
273+
return parseGraphQLParams(urlData, bodyData);
274+
});
275+
}
276+
261277
/**
262278
* Helper function to get the GraphQL params from the request.
263279
*/
264-
function getGraphQLParams(
280+
function parseGraphQLParams(
265281
urlData: {[param: string]: mixed},
266282
bodyData: {[param: string]: mixed}
267283
): GraphQLParams {
@@ -289,22 +305,22 @@ function getGraphQLParams(
289305
operationName = null;
290306
}
291307

292-
return { query, variables, operationName };
308+
const raw = urlData.raw !== undefined || bodyData.raw !== undefined;
309+
310+
return { query, variables, operationName, raw };
293311
}
294312

295313
/**
296314
* Helper function to determine if GraphiQL can be displayed.
297315
*/
298316
function canDisplayGraphiQL(
299317
request: Request,
300-
urlData: {[param: string]: mixed},
301-
bodyData: {[param: string]: mixed}
318+
params: GraphQLParams
302319
): boolean {
303320
// If `raw` exists, GraphiQL mode is not enabled.
304-
const raw = urlData.raw !== undefined || bodyData.raw !== undefined;
305321
// Allowed to show GraphiQL if not requested as raw and this request
306322
// prefers HTML over JSON.
307-
return !raw && accepts(request).types([ 'json', 'html' ]) === 'html';
323+
return !params.raw && accepts(request).types([ 'json', 'html' ]) === 'html';
308324
}
309325

310326
/**

src/parseBody.js

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,19 @@ import httpError from 'http-errors';
1414
import querystring from 'querystring';
1515
import zlib from 'zlib';
1616

17-
import type { Request } from 'express';
17+
import type { Request } from './index';
1818

19+
/**
20+
* Provided a "Request" provided by express or connect (typically a node style
21+
* HTTPClientRequest), Promise the body data contained.
22+
*/
1923
export function parseBody(req: Request): Promise<{[param: string]: mixed}> {
2024
return new Promise((resolve, reject) => {
25+
const body = req.body;
26+
2127
// If express has already parsed a body as a keyed object, use it.
22-
if (typeof req.body === 'object' && !(req.body instanceof Buffer)) {
23-
return resolve(req.body);
28+
if (typeof body === 'object' && !(body instanceof Buffer)) {
29+
return resolve((body: any));
2430
}
2531

2632
// Skip requests without content types.
@@ -32,13 +38,12 @@ export function parseBody(req: Request): Promise<{[param: string]: mixed}> {
3238

3339
// If express has already parsed a body as a string, and the content-type
3440
// was application/graphql, parse the string body.
35-
if (typeof req.body === 'string' &&
36-
typeInfo.type === 'application/graphql') {
37-
return resolve(graphqlParser(req.body));
41+
if (typeof body === 'string' && typeInfo.type === 'application/graphql') {
42+
return resolve(graphqlParser(body));
3843
}
3944

4045
// Already parsed body we didn't recognise? Parse nothing.
41-
if (req.body) {
46+
if (body) {
4247
return resolve({});
4348
}
4449

@@ -99,14 +104,16 @@ function read(req, typeInfo, parseFn, resolve, reject) {
99104
}
100105

101106
// Get content-encoding (e.g. gzip)
102-
const encoding =
103-
(req.headers['content-encoding'] || 'identity').toLowerCase();
107+
const contentEncoding = req.headers['content-encoding'];
108+
const encoding = typeof contentEncoding === 'string' ?
109+
contentEncoding.toLowerCase() :
110+
'identity';
104111
const length = encoding === 'identity' ? req.headers['content-length'] : null;
105112
const limit = 100 * 1024; // 100kb
106113
const stream = decompressed(req, encoding);
107114

108115
// Read body from stream.
109-
getBody(stream, { encoding: charset, length, limit }, function (err, body) {
116+
getBody(stream, { encoding: charset, length, limit }, (err, body) => {
110117
if (err) {
111118
return reject(
112119
err.type === 'encoding.unsupported' ?

0 commit comments

Comments
 (0)