express-graphql
Advanced tools
Comparing version 0.5.4 to 0.6.0
@@ -7,3 +7,3 @@ 'use strict'; | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; | ||
/** | ||
@@ -19,2 +19,3 @@ * Copyright (c) 2015, Facebook, Inc. | ||
exports.default = graphqlHTTP; | ||
exports.getGraphQLParams = getGraphQLParams; | ||
@@ -54,2 +55,7 @@ var _accepts = require('accepts'); | ||
*/ | ||
/** | ||
* All information about a GraphQL request. | ||
*/ | ||
function graphqlHTTP(options) { | ||
@@ -69,4 +75,6 @@ if (!options) { | ||
var formatErrorFn = void 0; | ||
var extensionsFn = void 0; | ||
var showGraphiQL = void 0; | ||
var query = void 0; | ||
var documentAST = void 0; | ||
var variables = void 0; | ||
@@ -100,2 +108,3 @@ var operationName = void 0; | ||
formatErrorFn = optionsData.formatError; | ||
extensionsFn = optionsData.extensions; | ||
@@ -113,13 +122,10 @@ validationRules = _graphql.specifiedRules; | ||
// Parse the Request body. | ||
return (0, _parseBody.parseBody)(request); | ||
}).then(function (bodyData) { | ||
var urlData = request.url && _url2.default.parse(request.url, true).query || {}; | ||
showGraphiQL = graphiql && canDisplayGraphiQL(request, urlData, bodyData); | ||
// Parse the Request to get GraphQL request parameters. | ||
return getGraphQLParams(request); | ||
}).then(function (params) { | ||
// Get GraphQL params from the request and POST body data. | ||
var params = getGraphQLParams(urlData, bodyData); | ||
query = params.query; | ||
variables = params.variables; | ||
operationName = params.operationName; | ||
showGraphiQL = graphiql && canDisplayGraphiQL(request, params); | ||
@@ -139,3 +145,2 @@ // If there is no query, but GraphiQL will be displayed, do not produce | ||
// Parse source to AST, reporting any syntax error. | ||
var documentAST = void 0; | ||
try { | ||
@@ -182,2 +187,19 @@ documentAST = (0, _graphql.parse)(source); | ||
} | ||
}).then(function (result) { | ||
// Collect and apply any metadata extensions if a function was provided. | ||
// http://facebook.github.io/graphql/#sec-Response-Format | ||
if (result && extensionsFn) { | ||
return Promise.resolve(extensionsFn({ | ||
document: documentAST, | ||
variables: variables, | ||
operationName: operationName, | ||
result: result | ||
})).then(function (extensions) { | ||
if (extensions && (typeof extensions === 'undefined' ? 'undefined' : _typeof(extensions)) === 'object') { | ||
result.extensions = extensions; | ||
} | ||
return result; | ||
}); | ||
} | ||
return result; | ||
}).catch(function (error) { | ||
@@ -188,2 +210,10 @@ // If an error was caught, report the httpError status, or 500. | ||
}).then(function (result) { | ||
// If no data was included in the result, that indicates a runtime query | ||
// error, indicate as such with a generic status code. | ||
// Note: Information about the error itself will still be contained in | ||
// the resulting JSON payload. | ||
// http://facebook.github.io/graphql/#sec-Data | ||
if (result && result.data === null) { | ||
response.statusCode = 500; | ||
} | ||
// Format any encountered errors. | ||
@@ -195,3 +225,3 @@ if (result && result.errors) { | ||
if (showGraphiQL) { | ||
var data = (0, _renderGraphiQL.renderGraphiQL)({ | ||
var payload = (0, _renderGraphiQL.renderGraphiQL)({ | ||
query: query, variables: variables, | ||
@@ -201,8 +231,8 @@ operationName: operationName, result: result | ||
response.setHeader('Content-Type', 'text/html; charset=utf-8'); | ||
response.end(data); | ||
sendResponse(response, payload); | ||
} else { | ||
// Otherwise, present JSON directly. | ||
var _data = JSON.stringify(result, null, pretty ? 2 : 0); | ||
var _payload = JSON.stringify(result, null, pretty ? 2 : 0); | ||
response.setHeader('Content-Type', 'application/json; charset=utf-8'); | ||
response.end(_data); | ||
sendResponse(response, _payload); | ||
} | ||
@@ -214,11 +244,25 @@ }); | ||
/** | ||
* Provided a "Request" provided by express or connect (typically a node style | ||
* HTTPClientRequest), Promise the GraphQL request parameters. | ||
*/ | ||
function getGraphQLParams(request) { | ||
return (0, _parseBody.parseBody)(request).then(function (bodyData) { | ||
var urlData = request.url && _url2.default.parse(request.url, true).query || {}; | ||
return parseGraphQLParams(urlData, bodyData); | ||
}); | ||
} | ||
/** | ||
* Helper function to get the GraphQL params from the request. | ||
*/ | ||
function getGraphQLParams(urlData, bodyData) { | ||
function parseGraphQLParams(urlData, bodyData) { | ||
// GraphQL Query string. | ||
var query = urlData.query || bodyData.query; | ||
if (typeof query !== 'string') { | ||
query = null; | ||
} | ||
// Parse the variables if needed. | ||
var variables = urlData.variables || bodyData.variables; | ||
if (variables && typeof variables === 'string') { | ||
if (typeof variables === 'string') { | ||
try { | ||
@@ -229,2 +273,4 @@ variables = JSON.parse(variables); | ||
} | ||
} else if ((typeof variables === 'undefined' ? 'undefined' : _typeof(variables)) !== 'object') { | ||
variables = null; | ||
} | ||
@@ -234,4 +280,9 @@ | ||
var operationName = urlData.operationName || bodyData.operationName; | ||
if (typeof operationName !== 'string') { | ||
operationName = null; | ||
} | ||
return { query: query, variables: variables, operationName: operationName }; | ||
var raw = urlData.raw !== undefined || bodyData.raw !== undefined; | ||
return { query: query, variables: variables, operationName: operationName, raw: raw }; | ||
} | ||
@@ -242,9 +293,19 @@ | ||
*/ | ||
function canDisplayGraphiQL(request, urlData, bodyData) { | ||
function canDisplayGraphiQL(request, params) { | ||
// If `raw` exists, GraphiQL mode is not enabled. | ||
var raw = urlData.raw !== undefined || bodyData.raw !== undefined; | ||
// Allowed to show GraphiQL if not requested as raw and this request | ||
// prefers HTML over JSON. | ||
return !raw && (0, _accepts2.default)(request).types(['json', 'html']) === 'html'; | ||
return !params.raw && (0, _accepts2.default)(request).types(['json', 'html']) === 'html'; | ||
} | ||
module.exports = exports['default']; | ||
/** | ||
* Helper function for sending the response data. Use response.send it method | ||
* exists (express), otherwise use response.end (connect). | ||
*/ | ||
function sendResponse(response, data) { | ||
if (typeof response.send === 'function') { | ||
response.send(data); | ||
} else { | ||
response.end(data); | ||
} | ||
} |
@@ -7,3 +7,3 @@ 'use strict'; | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; | ||
/** | ||
@@ -42,7 +42,13 @@ * Copyright (c) 2015, Facebook, Inc. | ||
/** | ||
* Provided a "Request" provided by express or connect (typically a node style | ||
* HTTPClientRequest), Promise the body data contained. | ||
*/ | ||
function parseBody(req) { | ||
return new Promise(function (resolve, reject) { | ||
var body = req.body; | ||
// If express has already parsed a body as a keyed object, use it. | ||
if (_typeof(req.body) === 'object' && !(req.body instanceof Buffer)) { | ||
return resolve(req.body); | ||
if ((typeof body === 'undefined' ? 'undefined' : _typeof(body)) === 'object' && !(body instanceof Buffer)) { | ||
return resolve(body); | ||
} | ||
@@ -59,8 +65,8 @@ | ||
// was application/graphql, parse the string body. | ||
if (typeof req.body === 'string' && typeInfo.type === 'application/graphql') { | ||
return resolve(graphqlParser(req.body)); | ||
if (typeof body === 'string' && typeInfo.type === 'application/graphql') { | ||
return resolve(graphqlParser(body)); | ||
} | ||
// Already parsed body we didn't recognise? Parse nothing. | ||
if (req.body) { | ||
if (body) { | ||
return resolve({}); | ||
@@ -126,3 +132,4 @@ } | ||
// Get content-encoding (e.g. gzip) | ||
var encoding = (req.headers['content-encoding'] || 'identity').toLowerCase(); | ||
var contentEncoding = req.headers['content-encoding']; | ||
var encoding = typeof contentEncoding === 'string' ? contentEncoding.toLowerCase() : 'identity'; | ||
var length = encoding === 'identity' ? req.headers['content-length'] : null; | ||
@@ -129,0 +136,0 @@ var limit = 100 * 1024; // 100kb |
@@ -24,3 +24,3 @@ 'use strict'; | ||
function safeSerialize(data) { | ||
return data ? JSON.stringify(data).replace(/\//g, '\\/') : null; | ||
return data ? JSON.stringify(data).replace(/\//g, '\\/') : 'undefined'; | ||
} | ||
@@ -27,0 +27,0 @@ |
{ | ||
"name": "express-graphql", | ||
"version": "0.5.4", | ||
"version": "0.6.0", | ||
"description": "Production ready GraphQL HTTP middleware.", | ||
@@ -56,3 +56,3 @@ "contributors": [ | ||
"build": "rm -rf dist/* && babel src --ignore __tests__ --out-dir dist", | ||
"watch": "babel --optional runtime resources/watch.js | node", | ||
"watch": "node resources/watch.js", | ||
"cover": "babel-node node_modules/.bin/isparta cover --root src --report html node_modules/.bin/_mocha -- $npm_package_options_mocha", | ||
@@ -69,34 +69,36 @@ "cover:lcov": "babel-node node_modules/.bin/isparta cover --root src --report lcovonly node_modules/.bin/_mocha -- $npm_package_options_mocha", | ||
"devDependencies": { | ||
"babel-cli": "^6.9.0", | ||
"babel-eslint": "6.0.4", | ||
"babel-plugin-add-module-exports": "^0.2.1", | ||
"babel-plugin-transform-async-to-generator": "6.8.0", | ||
"babel-plugin-transform-class-properties": "6.9.0", | ||
"babel-plugin-transform-flow-strip-types": "6.8.0", | ||
"babel-plugin-transform-runtime": "6.9.0", | ||
"babel-preset-es2015": "6.9.0", | ||
"babel-register": "6.9.0", | ||
"babel-runtime": "6.9.0", | ||
"body-parser": "1.15.1", | ||
"babel-cli": "6.18.0", | ||
"babel-eslint": "7.1.0", | ||
"babel-plugin-add-module-exports": "0.2.1", | ||
"babel-plugin-transform-async-to-generator": "6.16.0", | ||
"babel-plugin-transform-class-properties": "6.18.0", | ||
"babel-plugin-transform-flow-strip-types": "6.18.0", | ||
"babel-plugin-transform-runtime": "6.15.0", | ||
"babel-preset-es2015": "6.18.0", | ||
"babel-register": "6.18.0", | ||
"babel-runtime": "6.18.0", | ||
"body-parser": "1.15.2", | ||
"chai": "3.5.0", | ||
"connect": "3.4.1", | ||
"content-type": "1.0.1", | ||
"coveralls": "2.11.9", | ||
"eslint": "2.10.2", | ||
"eslint-plugin-babel": "3.2.0", | ||
"express": "4.13.4", | ||
"connect": "3.5.0", | ||
"content-type": "1.0.2", | ||
"coveralls": "2.11.15", | ||
"eslint": "3.10.0", | ||
"eslint-plugin-babel": "3.3.0", | ||
"eslint-plugin-flowtype": "2.25.0", | ||
"express": "4.14.0", | ||
"express3": "*", | ||
"flow-bin": "0.25.0", | ||
"graphql": "0.7.0", | ||
"flow-bin": "0.35.0", | ||
"graphql": "0.8.1", | ||
"isparta": "4.0.0", | ||
"mocha": "2.5.3", | ||
"multer": "1.1.0", | ||
"raw-body": "2.1.6", | ||
"sane": "1.3.4", | ||
"supertest": "1.0.1", | ||
"supertest-as-promised": "2.0.2" | ||
"mocha": "3.1.2", | ||
"multer": "1.2.0", | ||
"raw-body": "2.1.7", | ||
"sane": "1.4.1", | ||
"sinon": "1.17.6", | ||
"supertest": "2.0.1", | ||
"supertest-as-promised": "4.0.2" | ||
}, | ||
"peerDependencies": { | ||
"graphql": "^0.5.0-b || ^0.6.0 || ^0.7.0" | ||
"graphql": "^0.5.0-b || ^0.6.0 || ^0.7.0 || ^0.8.0-b" | ||
} | ||
} |
@@ -35,7 +35,7 @@ GraphQL HTTP Server Middleware | ||
* **`schema`**: A `GraphQLSchema` instance from [`graphql-js`][]. | ||
* **`schema`**: A `GraphQLSchema` instance from [`GraphQL.js`][]. | ||
A `schema` *must* be provided. | ||
* **`graphiql`**: If `true`, presents [GraphiQL][] when the route with a | ||
`/graphiql` appended is loaded in a browser. We recommend that you set | ||
* **`graphiql`**: If `true`, presents [GraphiQL][] when the GraphQL endpoint is | ||
loaded in a browser. We recommend that you set | ||
`graphiql` to `true` when your app is in development, because it's | ||
@@ -45,6 +45,6 @@ quite useful. You may or may not want it in production. | ||
* **`rootValue`**: A value to pass as the `rootValue` to the `graphql()` | ||
function from [`graphql-js`][]. | ||
function from [`GraphQL.js`][]. | ||
* **`context`**: A value to pass as the `context` to the `graphql()` | ||
function from [`graphql-js`][]. If `context` is not provided, the | ||
function from [`GraphQL.js`][]. If `context` is not provided, the | ||
`request` object is passed as the context. | ||
@@ -58,2 +58,9 @@ | ||
* **`extensions`**: An optional function for adding additional metadata to the | ||
GraphQL response as a key-value object. The result will be added to | ||
`"extensions"` field in the resulting JSON. This is often a useful place to | ||
add development time metadata such as the runtime of a query or the amount | ||
of resources consumed. This may be an async function. The function is | ||
give one object as an argument: `{ document, variables, operationName, result }`. | ||
* **`validationRules`**: Optional additional validation rules queries must | ||
@@ -147,2 +154,58 @@ satisfy in addition to those defined by the GraphQL spec. | ||
## Providing Extensions | ||
The GraphQL response allows for adding additional information in a response to | ||
a GraphQL query via a field in the response called `"extensions"`. This is added | ||
by providing an `extensions` function when using `graphqlHTTP`. The function | ||
must return a JSON-serializable Object. | ||
When called, this is provided an argument which you can use to get information | ||
about the GraphQL request: | ||
`{ document, variables, operationName, result }` | ||
This example illustrates adding the amount of time consumed by running the | ||
provided query, which could perhaps be used by your development tools. | ||
```js | ||
const graphqlHTTP = require('express-graphql'); | ||
const app = express(); | ||
app.use(session({ secret: 'keyboard cat', cookie: { maxAge: 60000 }})); | ||
app.use('/graphql', graphqlHTTP(request => { | ||
const startTime = Date.now(); | ||
return { | ||
schema: MyGraphQLSchema, | ||
graphiql: true, | ||
extensions({ document, variables, operationName, result }) { | ||
return { runTime: Date.now() - startTime }; | ||
} | ||
}; | ||
})); | ||
``` | ||
When querying this endpoint, it would include this information in the result, | ||
for example: | ||
```js | ||
{ | ||
"data": { ... } | ||
"extensions": { | ||
"runTime": 135 | ||
} | ||
} | ||
``` | ||
## Other Exports | ||
**`getGraphQLParams(request: Request): Promise<GraphQLParams>`** | ||
Given an HTTP Request, this returns a Promise for the parameters relevant to | ||
running a GraphQL request. This function is used internally to handle the | ||
incoming request, you may use it directly for building other similar services. | ||
## Debugging Tips | ||
@@ -162,3 +225,3 @@ | ||
[`graphql-js`]: https://github.com/graphql/graphql-js | ||
[`GraphQL.js`]: https://github.com/graphql/graphql-js | ||
[`formatError`]: https://github.com/graphql/graphql-js/blob/master/src/error/formatError.js | ||
@@ -165,0 +228,0 @@ [GraphiQL]: https://github.com/graphql/graphiql |
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
35017
427
226
30