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

Commit 65cbc54

Browse files
committed
Merge branch 'wsclient-option' of https://github.com/junminstorage/express-graphql into wsclient-option
2 parents 330bb63 + 3d3270e commit 65cbc54

File tree

5 files changed

+55
-102
lines changed

5 files changed

+55
-102
lines changed

.eslintrc.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ overrides:
491491
'@typescript-eslint/no-throw-literal': error
492492
'@typescript-eslint/no-type-alias': off # TODO consider
493493
'@typescript-eslint/no-unnecessary-boolean-literal-compare': error
494-
'@typescript-eslint/no-unnecessary-condition': off # TODO blocked by https://github.com/typescript-eslint/typescript-eslint/issues/2752
494+
'@typescript-eslint/no-unnecessary-condition': error
495495
'@typescript-eslint/no-unnecessary-qualifier': error
496496
'@typescript-eslint/no-unnecessary-type-arguments': error
497497
'@typescript-eslint/no-unnecessary-type-assertion': error

README.md

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -131,20 +131,20 @@ The `graphqlHTTP` function accepts the following options:
131131

132132
- **`defaultQuery`**: An optional GraphQL string to use when no query
133133
is provided and no stored query exists from a previous session.
134-
If undefined is provided, GraphiQL will use its own default query.
134+
If `undefined` is provided, GraphiQL will use its own default query.
135135

136-
- **`headerEditorEnabled`**: An optional boolean which enables the header editor when true.
137-
Defaults to false.
136+
- **`headerEditorEnabled`**: An optional boolean which enables the header editor when `true`.
137+
Defaults to `false`.
138138

139139
- **`subscriptionEndpoint`**: An optional GraphQL string contains the WebSocket server url for subscription.
140140

141141
- **`websocketClient`**: An optional GraphQL string for websocket client used for subscription, `v0`: subscriptions-transport-ws, `v1`: graphql-ws. Defaults to `v0` if not provided
142142

143-
- **`rootValue`**: A value to pass as the `rootValue` to the `graphql()`
144-
function from [`GraphQL.js/src/execute.js`](https://github.com/graphql/graphql-js/blob/master/src/execution/execute.js#L119).
143+
- **`rootValue`**: A value to pass as the `rootValue` to the `execute()`
144+
function from [`GraphQL.js/src/execute.js`](https://github.com/graphql/graphql-js/blob/main/src/execution/execute.js#L129).
145145

146-
- **`context`**: A value to pass as the `context` to the `graphql()`
147-
function from [`GraphQL.js/src/execute.js`](https://github.com/graphql/graphql-js/blob/master/src/execution/execute.js#L120). If `context` is not provided, the
146+
- **`context`**: A value to pass as the `contextValue` to the `execute()`
147+
function from [`GraphQL.js/src/execute.js`](https://github.com/graphql/graphql-js/blob/main/src/execution/execute.js#L130). If `context` is not provided, the
148148
`request` object is passed as the context.
149149

150150
- **`pretty`**: If `true`, any JSON response will be pretty-printed.
@@ -156,7 +156,7 @@ The `graphqlHTTP` function accepts the following options:
156156
of resources consumed. This may be an async function. The function is
157157
given one object as an argument: `{ document, variables, operationName, result, context }`.
158158

159-
- **`validationRules`**: Optional additional validation rules queries must
159+
- **`validationRules`**: Optional additional validation rules that queries must
160160
satisfy in addition to those defined by the GraphQL spec.
161161

162162
- **`customValidateFn`**: An optional function which will be used to validate
@@ -175,10 +175,6 @@ The `graphqlHTTP` function accepts the following options:
175175
- **`formatError`**: is deprecated and replaced by `customFormatErrorFn`. It will be
176176
removed in version 1.0.0.
177177

178-
- **`handleRuntimeQueryErrorFn`**: An optional function which can be used to change the status code
179-
or headers of the response in case of a runtime query error. By default, the status code is set to
180-
`500`.
181-
182178
In addition to an object defining each option, options can also be provided as
183179
a function (or async function) which returns this options object. This function
184180
is provided the arguments `(request, response, graphQLParams)` and is called
@@ -213,7 +209,7 @@ the parameters:
213209
named operations.
214210

215211
- **`raw`**: If the `graphiql` option is enabled and the `raw` parameter is
216-
provided raw JSON will always be returned instead of GraphiQL even when
212+
provided, raw JSON will always be returned instead of GraphiQL even when
217213
loaded from a browser.
218214

219215
GraphQL will first look for each parameter in the query string of a URL:
@@ -222,23 +218,23 @@ GraphQL will first look for each parameter in the query string of a URL:
222218
/graphql?query=query+getUser($id:ID){user(id:$id){name}}&variables={"id":"4"}
223219
```
224220

225-
If not found in the query-string, it will look in the POST request body.
221+
If not found in the query string, it will look in the POST request body.
226222

227223
If a previous middleware has already parsed the POST body, the `request.body`
228224
value will be used. Use [`multer`][] or a similar middleware to add support
229225
for `multipart/form-data` content, which may be useful for GraphQL mutations
230226
involving uploading files. See an [example using multer](https://github.com/graphql/express-graphql/blob/304b24b993c8f16fffff8d23b0fa4088e690874b/src/__tests__/http-test.js#L674-L741).
231227

232-
If the POST body has not yet been parsed, express-graphql will interpret it
228+
If the POST body has not yet been parsed, `express-graphql` will interpret it
233229
depending on the provided _Content-Type_ header.
234230

235231
- **`application/json`**: the POST body will be parsed as a JSON
236232
object of parameters.
237233

238-
- **`application/x-www-form-urlencoded`**: this POST body will be
234+
- **`application/x-www-form-urlencoded`**: the POST body will be
239235
parsed as a url-encoded string of key-value pairs.
240236

241-
- **`application/graphql`**: The POST body will be parsed as GraphQL
237+
- **`application/graphql`**: the POST body will be parsed as GraphQL
242238
query string, which provides the `query` parameter.
243239

244240
## Combining with Other Express Middleware
@@ -302,8 +298,6 @@ const { graphqlHTTP } = require('express-graphql');
302298

303299
const app = express();
304300

305-
app.use(session({ secret: 'keyboard cat', cookie: { maxAge: 60000 } }));
306-
307301
const extensions = ({
308302
document,
309303
variables,
@@ -334,7 +328,7 @@ for example:
334328

335329
```js
336330
{
337-
"data": { ... }
331+
"data": { ... },
338332
"extensions": {
339333
"runTime": 135
340334
}
@@ -345,7 +339,7 @@ for example:
345339

346340
GraphQL's [validation phase](https://graphql.github.io/graphql-spec/#sec-Validation) checks the query to ensure that it can be successfully executed against the schema. The `validationRules` option allows for additional rules to be run during this phase. Rules are applied to each node in an AST representing the query using the Visitor pattern.
347341

348-
A validation rule is a function which returns a visitor for one or more node Types. Below is an example of a validation preventing the specific field name `metadata` from being queried. For more examples see the [`specifiedRules`](https://github.com/graphql/graphql-js/tree/master/src/validation/rules) in the [graphql-js](https://github.com/graphql/graphql-js) package.
342+
A validation rule is a function which returns a visitor for one or more node Types. Below is an example of a validation preventing the specific field name `metadata` from being queried. For more examples, see the [`specifiedRules`](https://github.com/graphql/graphql-js/tree/main/src/validation/rules) in the [graphql-js](https://github.com/graphql/graphql-js) package.
349343

350344
```js
351345
import { GraphQLError } from 'graphql';
@@ -375,14 +369,14 @@ application any more secure. Nevertheless, disabling introspection is possible b
375369
package.
376370

377371
```js
378-
import { specifiedRules, NoSchemaIntrospectionCustomRule } from 'graphql';
372+
import { NoSchemaIntrospectionCustomRule } from 'graphql';
379373

380374
app.use(
381375
'/graphql',
382376
graphqlHTTP((request) => {
383377
return {
384378
schema: MyGraphQLSchema,
385-
validationRules: [...specifiedRules, NoSchemaIntrospectionCustomRule],
379+
validationRules: [NoSchemaIntrospectionCustomRule],
386380
};
387381
}),
388382
);
@@ -430,7 +424,17 @@ Each release of `express-graphql` will be accompanied by an experimental release
430424
Community feedback on this experimental release is much appreciated and can be provided on the [PR for the defer-stream branch](https://github.com/graphql/express-graphql/pull/726) or the [GraphQL.js issue for feedback](https://github.com/graphql/graphql-js/issues/2848).
431425

432426
[`graphql.js`]: https://github.com/graphql/graphql-js
433-
[`formaterror`]: https://github.com/graphql/graphql-js/blob/master/src/error/formatError.js
427+
[`formaterror`]: https://github.com/graphql/graphql-js/blob/main/src/error/formatError.js
434428
[graphiql]: https://github.com/graphql/graphiql
435429
[`multer`]: https://github.com/expressjs/multer
436430
[`express-session`]: https://github.com/expressjs/session
431+
432+
# Contributing to this repo
433+
434+
This repository is managed by EasyCLA. Project participants must sign the free [GraphQL Specification Membership agreement](https://preview-spec-membership.graphql.org) before making a contribution. You only need to do this one time, and it can be signed by [individual contributors](http://individual-spec-membership.graphql.org/) or their [employers](http://corporate-spec-membership.graphql.org/).
435+
436+
To initiate the signature process please open a PR against this repo. The EasyCLA bot will block the merge if we still need a membership agreement from you.
437+
438+
You can find [detailed information here](https://github.com/graphql/graphql-wg/tree/main/membership). If you have issues, please email [operations@graphql.org](mailto:operations@graphql.org).
439+
440+
If your company benefits from GraphQL and you would like to provide essential financial support for the systems and people that power our community, please also consider membership in the [GraphQL Foundation](https://foundation.graphql.org/join).

integrationTests/integration-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ function exec(command, options = {}) {
1717

1818
describe('Integration Tests', () => {
1919
const tmpDir = path.join(os.tmpdir(), 'express-graphql-integrationTmp');
20-
fs.rmdirSync(tmpDir, { recursive: true });
20+
fs.rmdirSync(tmpDir, { recursive: true, force: true });
2121
fs.mkdirSync(tmpDir);
2222

2323
const distDir = path.resolve('./npmDist');

src/__tests__/usage-test.ts

Lines changed: 1 addition & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,7 @@ import express from 'express';
22
import request from 'supertest';
33
import { expect } from 'chai';
44
import { describe, it } from 'mocha';
5-
import {
6-
GraphQLNonNull,
7-
GraphQLObjectType,
8-
GraphQLSchema,
9-
GraphQLString,
10-
} from 'graphql';
5+
import { GraphQLSchema } from 'graphql';
116

127
import { graphqlHTTP } from '../index';
138

@@ -101,40 +96,6 @@ describe('Useful errors when incorrectly used', () => {
10196
});
10297
});
10398

104-
it('uses the custom runtime query error handling function', async () => {
105-
const schema = new GraphQLSchema({
106-
query: new GraphQLObjectType({
107-
name: 'QueryRoot',
108-
fields: {
109-
test: {
110-
type: new GraphQLNonNull(GraphQLString),
111-
resolve() {
112-
throw new Error('Throws!');
113-
},
114-
},
115-
},
116-
}),
117-
});
118-
119-
const app = express();
120-
121-
app.use(
122-
'/graphql',
123-
graphqlHTTP({
124-
handleRuntimeQueryErrorFn(_, response) {
125-
response.setHeader('customRuntimeQueryError', "I'm a teapot");
126-
response.statusCode = 418;
127-
},
128-
schema,
129-
}),
130-
);
131-
132-
const response = await request(app).get('/graphql?query={test}');
133-
134-
expect(response.status).to.equal(418);
135-
expect(response.get('customRuntimeQueryError')).to.equal("I'm a teapot");
136-
});
137-
13899
it('validates schema before executing request', async () => {
139100
// @ts-expect-error
140101
const schema = new GraphQLSchema({ directives: [null] });

src/index.ts

Lines changed: 24 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -115,15 +115,6 @@ export interface OptionsData {
115115
*/
116116
formatError?: (error: GraphQLError) => GraphQLFormattedError;
117117

118-
/**
119-
* Use this to modify the response when a runtime query error occurs. By
120-
* default the statusCode will be set to 500.
121-
*/
122-
handleRuntimeQueryErrorFn?: (
123-
result: ExecutionResult,
124-
response: Response,
125-
) => void;
126-
127118
/**
128119
* An optional function for adding additional metadata to the GraphQL response
129120
* as a key-value object. The result will be added to "extensions" field in
@@ -196,7 +187,7 @@ type Middleware = (request: Request, response: Response) => Promise<void>;
196187
* configure behavior, and returns an express middleware.
197188
*/
198189
export function graphqlHTTP(options: Options): Middleware {
199-
devAssert(options != null, 'GraphQL middleware requires options.');
190+
devAssertIsNonNullable(options, 'GraphQL middleware requires options.');
200191

201192
return async function graphqlMiddleware(
202193
request: Request,
@@ -209,7 +200,6 @@ export function graphqlHTTP(options: Options): Middleware {
209200
let formatErrorFn = formatError;
210201
let pretty = false;
211202
let result: ExecutionResult;
212-
let optionsData: OptionsData | undefined;
213203

214204
try {
215205
// Parse the Request to get GraphQL request parameters.
@@ -218,7 +208,7 @@ export function graphqlHTTP(options: Options): Middleware {
218208
} catch (error: unknown) {
219209
// When we failed to parse the GraphQL parameters, we still need to get
220210
// the options object, so make an options call to resolve just that.
221-
optionsData = await resolveOptions();
211+
const optionsData = await resolveOptions();
222212
pretty = optionsData.pretty ?? false;
223213
formatErrorFn =
224214
optionsData.customFormatErrorFn ??
@@ -228,7 +218,7 @@ export function graphqlHTTP(options: Options): Middleware {
228218
}
229219

230220
// Then, resolve the Options to get OptionsData.
231-
optionsData = await resolveOptions(params);
221+
const optionsData: OptionsData = await resolveOptions(params);
232222

233223
// Collect information from the options data object.
234224
const schema = optionsData.schema;
@@ -249,9 +239,8 @@ export function graphqlHTTP(options: Options): Middleware {
249239
optionsData.formatError ??
250240
formatErrorFn;
251241

252-
// Assert that schema is required.
253-
devAssert(
254-
schema != null,
242+
devAssertIsObject(
243+
schema,
255244
'GraphQL middleware options must contain a schema.',
256245
);
257246

@@ -398,22 +387,13 @@ export function graphqlHTTP(options: Options): Middleware {
398387
}
399388
}
400389

401-
if (result.errors != null || result.data == null) {
402-
const handleRuntimeQueryErrorFn =
403-
optionsData?.handleRuntimeQueryErrorFn ??
404-
((_result, _response) => {
405-
// If no data was included in the result, that indicates a runtime query
406-
// error, indicate as such with a generic status code.
407-
// Note: Information about the error itself will still be contained in
408-
// the resulting JSON payload.
409-
// https://graphql.github.io/graphql-spec/#sec-Data
410-
411-
if (_response.statusCode === 200 && _result.data == null) {
412-
_response.statusCode = 500;
413-
}
414-
});
415-
416-
handleRuntimeQueryErrorFn(result, response);
390+
// If no data was included in the result, that indicates a runtime query
391+
// error, indicate as such with a generic status code.
392+
// Note: Information about the error itself will still be contained in
393+
// the resulting JSON payload.
394+
// https://graphql.github.io/graphql-spec/#sec-Data
395+
if (response.statusCode === 200 && result.data == null) {
396+
response.statusCode = 500;
417397
}
418398

419399
// Format any encountered errors.
@@ -451,8 +431,8 @@ export function graphqlHTTP(options: Options): Middleware {
451431
: options,
452432
);
453433

454-
devAssert(
455-
optionsResult != null && typeof optionsResult === 'object',
434+
devAssertIsObject(
435+
optionsResult,
456436
'GraphQL middleware option function must return an options object or a promise which will be resolved to an options object.',
457437
);
458438

@@ -552,9 +532,17 @@ function sendResponse(response: Response, type: string, data: string): void {
552532
response.end(chunk);
553533
}
554534

555-
function devAssert(condition: unknown, message: string): asserts condition {
535+
function devAssertIsObject(value: unknown, message: string): void {
536+
devAssert(value != null && typeof value === 'object', message);
537+
}
538+
539+
function devAssertIsNonNullable(value: unknown, message: string): void {
540+
devAssert(value != null, message);
541+
}
542+
543+
function devAssert(condition: unknown, message: string): void {
556544
const booleanCondition = Boolean(condition);
557545
if (!booleanCondition) {
558-
throw new Error(message);
546+
throw new TypeError(message);
559547
}
560548
}

0 commit comments

Comments
 (0)