graphql-http 0.1.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{LICENSE → LICENSE.md} +2 -2
- package/README.md +693 -30
- package/lib/client.d.ts +155 -0
- package/lib/client.js +189 -0
- package/lib/client.mjs +170 -0
- package/lib/common.d.ts +108 -0
- package/lib/common.js +21 -0
- package/lib/common.mjs +17 -0
- package/lib/handler.d.ts +166 -0
- package/lib/handler.js +358 -0
- package/lib/handler.mjs +354 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +18 -184
- package/lib/index.mjs +3 -0
- package/lib/utils.d.ts +10 -0
- package/lib/utils.js +22 -0
- package/lib/utils.mjs +17 -0
- package/package.json +87 -25
- package/umd/graphql-http.js +208 -0
- package/umd/graphql-http.min.js +1 -0
- package/umd/graphql-http.min.js.gz +0 -0
- package/.eslintrc.json +0 -8
- package/.npmignore +0 -33
- package/index.js +0 -1
- package/lib/__tests__/index.js +0 -66
package/lib/handler.d.ts
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* handler
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
import { ExecutionArgs, ExecutionResult, GraphQLSchema, validate as graphqlValidate, execute as graphqlExecute, parse as graphqlParse, getOperationAST as graphqlGetOperationAST, GraphQLError } from 'graphql';
|
|
7
|
+
import { Request, RequestParams, Response } from './common';
|
|
8
|
+
/**
|
|
9
|
+
* A concrete GraphQL execution context value type.
|
|
10
|
+
*
|
|
11
|
+
* Mainly used because TypeScript collapes unions
|
|
12
|
+
* with `any` or `unknown` to `any` or `unknown`. So,
|
|
13
|
+
* we use a custom type to allow definitions such as
|
|
14
|
+
* the `context` server option.
|
|
15
|
+
*
|
|
16
|
+
* @category Server
|
|
17
|
+
*/
|
|
18
|
+
export declare type ExecutionContext = object | symbol | number | string | boolean | undefined | null;
|
|
19
|
+
/** @category Server */
|
|
20
|
+
export interface HandlerOptions<RawRequest = unknown> {
|
|
21
|
+
/**
|
|
22
|
+
* The GraphQL schema on which the operations will
|
|
23
|
+
* be executed and validated against.
|
|
24
|
+
*
|
|
25
|
+
* If a function is provided, it will be called on every
|
|
26
|
+
* operation request allowing you to manipulate schema
|
|
27
|
+
* dynamically.
|
|
28
|
+
*
|
|
29
|
+
* If the schema is left undefined, you're trusted to
|
|
30
|
+
* provide one in the returned `ExecutionArgs` from the
|
|
31
|
+
* `onSubscribe` callback.
|
|
32
|
+
*
|
|
33
|
+
* If you want to respond to the client with a custom status and/or body,
|
|
34
|
+
* you should do by returning a `Request` argument which will stop
|
|
35
|
+
* further execution.
|
|
36
|
+
*/
|
|
37
|
+
schema?: GraphQLSchema | ((req: Request<RawRequest>, args: Omit<ExecutionArgs, 'schema'>) => Promise<GraphQLSchema | Response> | GraphQLSchema | Response);
|
|
38
|
+
/**
|
|
39
|
+
* A value which is provided to every resolver and holds
|
|
40
|
+
* important contextual information like the currently
|
|
41
|
+
* logged in user, or access to a database.
|
|
42
|
+
*/
|
|
43
|
+
context?: ExecutionContext | ((req: Request<RawRequest>, args: ExecutionArgs) => Promise<ExecutionContext | Response> | ExecutionContext | Response);
|
|
44
|
+
/**
|
|
45
|
+
* A custom GraphQL validate function allowing you to apply your
|
|
46
|
+
* own validation rules.
|
|
47
|
+
*/
|
|
48
|
+
validate?: typeof graphqlValidate;
|
|
49
|
+
/**
|
|
50
|
+
* Is the `execute` function from GraphQL which is
|
|
51
|
+
* used to execute the query and mutation operations.
|
|
52
|
+
*/
|
|
53
|
+
execute?: typeof graphqlExecute;
|
|
54
|
+
/**
|
|
55
|
+
* GraphQL parse function allowing you to apply a custom parser.
|
|
56
|
+
*/
|
|
57
|
+
parse?: typeof graphqlParse;
|
|
58
|
+
/**
|
|
59
|
+
* GraphQL operation AST getter used for detecting the operation type.
|
|
60
|
+
*/
|
|
61
|
+
getOperationAST?: typeof graphqlGetOperationAST;
|
|
62
|
+
/**
|
|
63
|
+
* The subscribe callback executed right after processing the request
|
|
64
|
+
* before proceeding with the GraphQL operation execution.
|
|
65
|
+
*
|
|
66
|
+
* If you return `ExecutionArgs` from the callback, it will be used instead of
|
|
67
|
+
* trying to build one internally. In this case, you are responsible for providing
|
|
68
|
+
* a ready set of arguments which will be directly plugged in the operation execution.
|
|
69
|
+
*
|
|
70
|
+
* If you return an array of `GraphQLError` from the callback, they will be reported
|
|
71
|
+
* to the client while complying with the spec.
|
|
72
|
+
*
|
|
73
|
+
* Omitting the fields `contextValue` from the returned `ExecutionArgs` will use the
|
|
74
|
+
* provided `context` option, if available.
|
|
75
|
+
*
|
|
76
|
+
* Useful for preparing the execution arguments following a custom logic. A typical
|
|
77
|
+
* use-case is persisted queries. You can identify the query from the request parameters
|
|
78
|
+
* and supply the appropriate GraphQL operation execution arguments.
|
|
79
|
+
*
|
|
80
|
+
* If you want to respond to the client with a custom status and/or body,
|
|
81
|
+
* you should do by returning a `Request` argument which will stop
|
|
82
|
+
* further execution.
|
|
83
|
+
*/
|
|
84
|
+
onSubscribe?: (req: Request<RawRequest>, params: RequestParams) => Promise<ExecutionArgs | GraphQLError[] | Response | void> | ExecutionArgs | GraphQLError[] | Response | void;
|
|
85
|
+
/**
|
|
86
|
+
* Executed after the operation call resolves.
|
|
87
|
+
*
|
|
88
|
+
* The `OperationResult` argument is the result of operation
|
|
89
|
+
* execution. It can be an iterator or already a value.
|
|
90
|
+
*
|
|
91
|
+
* Use this callback to listen for GraphQL operations and
|
|
92
|
+
* execution result manipulation.
|
|
93
|
+
*
|
|
94
|
+
* If you want to respond to the client with a custom status and/or body,
|
|
95
|
+
* you should do by returning a `Request` argument which will stop
|
|
96
|
+
* further execution.
|
|
97
|
+
*/
|
|
98
|
+
onOperation?: (req: Request<RawRequest>, args: ExecutionArgs, result: ExecutionResult) => Promise<ExecutionResult | Response | void> | ExecutionResult | Response | void;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The ready-to-use handler. Simply plug it in your favourite HTTP framework
|
|
102
|
+
* and enjoy.
|
|
103
|
+
*
|
|
104
|
+
* Errors thrown from **any** of the provided options or callbacks (or even due to
|
|
105
|
+
* library misuse or potential bugs) will reject the handler's promise. They are
|
|
106
|
+
* considered internal errors and you should take care of them accordingly.
|
|
107
|
+
*
|
|
108
|
+
* @category Server
|
|
109
|
+
*/
|
|
110
|
+
export declare type Handler<RawRequest = unknown> = (req: Request<RawRequest>) => Promise<Response>;
|
|
111
|
+
/**
|
|
112
|
+
* Makes a GraphQL over HTTP Protocol compliant server handler. The handler can
|
|
113
|
+
* be used with your favourite server library.
|
|
114
|
+
*
|
|
115
|
+
* Beware that the handler resolves only after the whole operation completes.
|
|
116
|
+
*
|
|
117
|
+
* Errors thrown from **any** of the provided options or callbacks (or even due to
|
|
118
|
+
* library misuse or potential bugs) will reject the handler's promise. They are
|
|
119
|
+
* considered internal errors and you should take care of them accordingly.
|
|
120
|
+
*
|
|
121
|
+
* For production environments, its recommended not to transmit the exact internal
|
|
122
|
+
* error details to the client, but instead report to an error logging tool or simply
|
|
123
|
+
* the console.
|
|
124
|
+
*
|
|
125
|
+
* Simple example usage with Node:
|
|
126
|
+
*
|
|
127
|
+
* ```js
|
|
128
|
+
* import http from 'http';
|
|
129
|
+
* import { createHandler } from 'graphql-http';
|
|
130
|
+
* import { schema } from './my-graphql-schema';
|
|
131
|
+
*
|
|
132
|
+
* // Create the GraphQL over HTTP handler
|
|
133
|
+
* const handler = createHandler({ schema });
|
|
134
|
+
*
|
|
135
|
+
* // Create a HTTP server using the handler on `/graphql`
|
|
136
|
+
* const server = http.createServer(async (req, res) => {
|
|
137
|
+
* if (!req.url.startsWith('/graphql')) {
|
|
138
|
+
* return res.writeHead(404).end();
|
|
139
|
+
* }
|
|
140
|
+
*
|
|
141
|
+
* try {
|
|
142
|
+
* const [body, init] = await handler({
|
|
143
|
+
* url: req.url,
|
|
144
|
+
* method: req.method,
|
|
145
|
+
* headers: req.headers,
|
|
146
|
+
* body: await new Promise((resolve) => {
|
|
147
|
+
* let body = '';
|
|
148
|
+
* req.on('data', (chunk) => (body += chunk));
|
|
149
|
+
* req.on('end', () => resolve(body));
|
|
150
|
+
* }),
|
|
151
|
+
* raw: req,
|
|
152
|
+
* });
|
|
153
|
+
* res.writeHead(init.status, init.statusText, init.headers).end(body);
|
|
154
|
+
* } catch (err) {
|
|
155
|
+
* // BEWARE not to transmit the exact internal error message in production environments
|
|
156
|
+
* res.writeHead(500).end(err.message);
|
|
157
|
+
* }
|
|
158
|
+
* });
|
|
159
|
+
*
|
|
160
|
+
* server.listen(4000);
|
|
161
|
+
* console.log('Listening to port 4000');
|
|
162
|
+
* ```
|
|
163
|
+
*
|
|
164
|
+
* @category Server
|
|
165
|
+
*/
|
|
166
|
+
export declare function createHandler<RawRequest = unknown>(options: HandlerOptions<RawRequest>): Handler<RawRequest>;
|
package/lib/handler.js
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
4
|
+
* handler
|
|
5
|
+
*
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.createHandler = void 0;
|
|
9
|
+
const graphql_1 = require("graphql");
|
|
10
|
+
const common_1 = require("./common");
|
|
11
|
+
const utils_1 = require("./utils");
|
|
12
|
+
/**
|
|
13
|
+
* Makes a GraphQL over HTTP Protocol compliant server handler. The handler can
|
|
14
|
+
* be used with your favourite server library.
|
|
15
|
+
*
|
|
16
|
+
* Beware that the handler resolves only after the whole operation completes.
|
|
17
|
+
*
|
|
18
|
+
* Errors thrown from **any** of the provided options or callbacks (or even due to
|
|
19
|
+
* library misuse or potential bugs) will reject the handler's promise. They are
|
|
20
|
+
* considered internal errors and you should take care of them accordingly.
|
|
21
|
+
*
|
|
22
|
+
* For production environments, its recommended not to transmit the exact internal
|
|
23
|
+
* error details to the client, but instead report to an error logging tool or simply
|
|
24
|
+
* the console.
|
|
25
|
+
*
|
|
26
|
+
* Simple example usage with Node:
|
|
27
|
+
*
|
|
28
|
+
* ```js
|
|
29
|
+
* import http from 'http';
|
|
30
|
+
* import { createHandler } from 'graphql-http';
|
|
31
|
+
* import { schema } from './my-graphql-schema';
|
|
32
|
+
*
|
|
33
|
+
* // Create the GraphQL over HTTP handler
|
|
34
|
+
* const handler = createHandler({ schema });
|
|
35
|
+
*
|
|
36
|
+
* // Create a HTTP server using the handler on `/graphql`
|
|
37
|
+
* const server = http.createServer(async (req, res) => {
|
|
38
|
+
* if (!req.url.startsWith('/graphql')) {
|
|
39
|
+
* return res.writeHead(404).end();
|
|
40
|
+
* }
|
|
41
|
+
*
|
|
42
|
+
* try {
|
|
43
|
+
* const [body, init] = await handler({
|
|
44
|
+
* url: req.url,
|
|
45
|
+
* method: req.method,
|
|
46
|
+
* headers: req.headers,
|
|
47
|
+
* body: await new Promise((resolve) => {
|
|
48
|
+
* let body = '';
|
|
49
|
+
* req.on('data', (chunk) => (body += chunk));
|
|
50
|
+
* req.on('end', () => resolve(body));
|
|
51
|
+
* }),
|
|
52
|
+
* raw: req,
|
|
53
|
+
* });
|
|
54
|
+
* res.writeHead(init.status, init.statusText, init.headers).end(body);
|
|
55
|
+
* } catch (err) {
|
|
56
|
+
* // BEWARE not to transmit the exact internal error message in production environments
|
|
57
|
+
* res.writeHead(500).end(err.message);
|
|
58
|
+
* }
|
|
59
|
+
* });
|
|
60
|
+
*
|
|
61
|
+
* server.listen(4000);
|
|
62
|
+
* console.log('Listening to port 4000');
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* @category Server
|
|
66
|
+
*/
|
|
67
|
+
function createHandler(options) {
|
|
68
|
+
const { schema, context, validate = graphql_1.validate, execute = graphql_1.execute, parse = graphql_1.parse, getOperationAST = graphql_1.getOperationAST, onSubscribe, onOperation, } = options;
|
|
69
|
+
return async function handler(req) {
|
|
70
|
+
var _a, _b;
|
|
71
|
+
const method = req.method;
|
|
72
|
+
if (method !== 'GET' && method !== 'POST') {
|
|
73
|
+
return [
|
|
74
|
+
null,
|
|
75
|
+
{
|
|
76
|
+
status: 405,
|
|
77
|
+
statusText: 'Method Not Allowed',
|
|
78
|
+
headers: {
|
|
79
|
+
allow: 'GET, POST',
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
];
|
|
83
|
+
}
|
|
84
|
+
let acceptedMediaType;
|
|
85
|
+
const accepts = (req.headers.accept || '*/*')
|
|
86
|
+
.replace(/\s/g, '')
|
|
87
|
+
.toLowerCase()
|
|
88
|
+
.split(',');
|
|
89
|
+
for (const accept of accepts) {
|
|
90
|
+
// accept-charset became obsolete, shouldnt be used (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Charset)
|
|
91
|
+
// TODO: handle the weight parameter "q"
|
|
92
|
+
const [mediaType, ...params] = accept.split(';');
|
|
93
|
+
const charset = (params === null || params === void 0 ? void 0 : params.find((param) => param.includes('charset='))) || 'charset=utf8'; // utf-8 is assumed when not specified;
|
|
94
|
+
if (mediaType === 'application/json' && charset === 'charset=utf8') {
|
|
95
|
+
acceptedMediaType = 'application/json';
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
if ((mediaType === 'application/graphql+json' ||
|
|
99
|
+
mediaType === 'application/*' ||
|
|
100
|
+
mediaType === '*/*') &&
|
|
101
|
+
charset === 'charset=utf8') {
|
|
102
|
+
acceptedMediaType = 'application/graphql+json';
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (!acceptedMediaType) {
|
|
107
|
+
return [
|
|
108
|
+
null,
|
|
109
|
+
{
|
|
110
|
+
status: 406,
|
|
111
|
+
statusText: 'Not Acceptable',
|
|
112
|
+
headers: {
|
|
113
|
+
accept: 'application/graphql+json; charset=utf-8, application/json; charset=utf-8',
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
];
|
|
117
|
+
}
|
|
118
|
+
// TODO: should graphql-http care about content-encoding? I'd say unzipping should happen before handler is reached
|
|
119
|
+
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)
|
|
120
|
+
] = (req.headers['content-type'] || '')
|
|
121
|
+
.replace(/\s/g, '')
|
|
122
|
+
.toLowerCase()
|
|
123
|
+
.split(';');
|
|
124
|
+
let params;
|
|
125
|
+
try {
|
|
126
|
+
const partParams = {};
|
|
127
|
+
switch (true) {
|
|
128
|
+
case method === 'GET': {
|
|
129
|
+
// TODO: what if content-type is specified and is not application/x-www-form-urlencoded?
|
|
130
|
+
try {
|
|
131
|
+
const [, search] = req.url.split('?');
|
|
132
|
+
const searchParams = new URLSearchParams(search);
|
|
133
|
+
partParams.operationName =
|
|
134
|
+
(_a = searchParams.get('operationName')) !== null && _a !== void 0 ? _a : undefined;
|
|
135
|
+
partParams.query = (_b = searchParams.get('query')) !== null && _b !== void 0 ? _b : undefined;
|
|
136
|
+
const variables = searchParams.get('variables');
|
|
137
|
+
if (variables)
|
|
138
|
+
partParams.variables = JSON.parse(variables);
|
|
139
|
+
const extensions = searchParams.get('extensions');
|
|
140
|
+
if (extensions)
|
|
141
|
+
partParams.extensions = JSON.parse(extensions);
|
|
142
|
+
}
|
|
143
|
+
catch (_c) {
|
|
144
|
+
throw new Error('Unparsable URL');
|
|
145
|
+
}
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
case method === 'POST' &&
|
|
149
|
+
mediaType === 'application/json' &&
|
|
150
|
+
charset === 'charset=utf-8':
|
|
151
|
+
{
|
|
152
|
+
if (!req.body) {
|
|
153
|
+
throw new Error('Missing body');
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
const data = typeof req.body === 'string' ? JSON.parse(req.body) : req.body;
|
|
157
|
+
partParams.operationName = data.operationName;
|
|
158
|
+
partParams.query = data.query;
|
|
159
|
+
partParams.variables = data.variables;
|
|
160
|
+
partParams.extensions = data.extensions;
|
|
161
|
+
}
|
|
162
|
+
catch (_d) {
|
|
163
|
+
throw new Error('Unparsable JSON body');
|
|
164
|
+
}
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
default: // graphql-http doesnt support any other content type
|
|
168
|
+
return [
|
|
169
|
+
null,
|
|
170
|
+
{
|
|
171
|
+
status: 415,
|
|
172
|
+
statusText: 'Unsupported Media Type',
|
|
173
|
+
},
|
|
174
|
+
];
|
|
175
|
+
}
|
|
176
|
+
if (!partParams.query)
|
|
177
|
+
throw new Error('Missing query');
|
|
178
|
+
if (partParams.variables != null &&
|
|
179
|
+
(typeof partParams.variables !== 'object' ||
|
|
180
|
+
Array.isArray(partParams.variables))) {
|
|
181
|
+
throw new Error('Invalid variables');
|
|
182
|
+
}
|
|
183
|
+
if (partParams.extensions != null &&
|
|
184
|
+
(typeof partParams.extensions !== 'object' ||
|
|
185
|
+
Array.isArray(partParams.extensions))) {
|
|
186
|
+
throw new Error('Invalid extensions');
|
|
187
|
+
}
|
|
188
|
+
// request parameters are checked and now complete
|
|
189
|
+
params = partParams;
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
return [
|
|
193
|
+
err.message,
|
|
194
|
+
Object.assign(Object.assign({}, (acceptedMediaType === 'application/json'
|
|
195
|
+
? {
|
|
196
|
+
status: 200,
|
|
197
|
+
statusText: 'OK',
|
|
198
|
+
}
|
|
199
|
+
: {
|
|
200
|
+
status: 400,
|
|
201
|
+
statusText: 'Bad Request',
|
|
202
|
+
})), { statusText: 'Bad Request' }),
|
|
203
|
+
];
|
|
204
|
+
}
|
|
205
|
+
let args;
|
|
206
|
+
const maybeResErrsOrArgs = await (onSubscribe === null || onSubscribe === void 0 ? void 0 : onSubscribe(req, params));
|
|
207
|
+
if ((0, common_1.isResponse)(maybeResErrsOrArgs))
|
|
208
|
+
return maybeResErrsOrArgs;
|
|
209
|
+
else if ((0, utils_1.areGraphQLErrors)(maybeResErrsOrArgs))
|
|
210
|
+
return [
|
|
211
|
+
JSON.stringify({ errors: maybeResErrsOrArgs }),
|
|
212
|
+
Object.assign(Object.assign({}, (acceptedMediaType === 'application/json'
|
|
213
|
+
? {
|
|
214
|
+
status: 200,
|
|
215
|
+
statusText: 'OK',
|
|
216
|
+
}
|
|
217
|
+
: {
|
|
218
|
+
status: 400,
|
|
219
|
+
statusText: 'Bad Request',
|
|
220
|
+
})), { headers: {
|
|
221
|
+
'content-type': acceptedMediaType === 'application/json'
|
|
222
|
+
? 'application/json; charset=utf-8'
|
|
223
|
+
: 'application/graphql+json; charset=utf-8',
|
|
224
|
+
} }),
|
|
225
|
+
];
|
|
226
|
+
else if (maybeResErrsOrArgs)
|
|
227
|
+
args = maybeResErrsOrArgs;
|
|
228
|
+
else {
|
|
229
|
+
if (!schema)
|
|
230
|
+
throw new Error('The GraphQL schema is not provided');
|
|
231
|
+
const { operationName, query, variables } = params;
|
|
232
|
+
let document;
|
|
233
|
+
try {
|
|
234
|
+
document = parse(query);
|
|
235
|
+
}
|
|
236
|
+
catch (err) {
|
|
237
|
+
return [
|
|
238
|
+
JSON.stringify({ errors: [err] }),
|
|
239
|
+
Object.assign(Object.assign({}, (acceptedMediaType === 'application/json'
|
|
240
|
+
? {
|
|
241
|
+
status: 200,
|
|
242
|
+
statusText: 'OK',
|
|
243
|
+
}
|
|
244
|
+
: {
|
|
245
|
+
status: 400,
|
|
246
|
+
statusText: 'Bad Request',
|
|
247
|
+
})), { statusText: 'Bad Request', headers: {
|
|
248
|
+
'content-type': acceptedMediaType === 'application/json'
|
|
249
|
+
? 'application/json; charset=utf-8'
|
|
250
|
+
: 'application/graphql+json; charset=utf-8',
|
|
251
|
+
} }),
|
|
252
|
+
];
|
|
253
|
+
}
|
|
254
|
+
const argsWithoutSchema = {
|
|
255
|
+
operationName,
|
|
256
|
+
document,
|
|
257
|
+
variableValues: variables,
|
|
258
|
+
};
|
|
259
|
+
if (typeof schema === 'function') {
|
|
260
|
+
const resOrSchema = await schema(req, argsWithoutSchema);
|
|
261
|
+
if ((0, common_1.isResponse)(resOrSchema))
|
|
262
|
+
return resOrSchema;
|
|
263
|
+
args = Object.assign(Object.assign({}, argsWithoutSchema), { schema: resOrSchema });
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
args = Object.assign(Object.assign({}, argsWithoutSchema), { schema });
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
let operation;
|
|
270
|
+
try {
|
|
271
|
+
const ast = getOperationAST(args.document, args.operationName);
|
|
272
|
+
if (!ast)
|
|
273
|
+
throw null;
|
|
274
|
+
operation = ast.operation;
|
|
275
|
+
}
|
|
276
|
+
catch (_e) {
|
|
277
|
+
return [
|
|
278
|
+
'Unable to detect operation AST',
|
|
279
|
+
Object.assign(Object.assign({}, (acceptedMediaType === 'application/json'
|
|
280
|
+
? {
|
|
281
|
+
status: 200,
|
|
282
|
+
statusText: 'OK',
|
|
283
|
+
}
|
|
284
|
+
: {
|
|
285
|
+
status: 400,
|
|
286
|
+
statusText: 'Bad Request',
|
|
287
|
+
})), { statusText: 'Bad Request' }),
|
|
288
|
+
];
|
|
289
|
+
}
|
|
290
|
+
if (operation === 'subscription') {
|
|
291
|
+
return [
|
|
292
|
+
'Subscriptions are not supported',
|
|
293
|
+
{
|
|
294
|
+
status: 400,
|
|
295
|
+
statusText: 'Bad Request',
|
|
296
|
+
},
|
|
297
|
+
];
|
|
298
|
+
}
|
|
299
|
+
// mutations cannot happen over GETs
|
|
300
|
+
// https://graphql.github.io/graphql-over-http/draft/#sel-CALFJRPAAELBAAxwP
|
|
301
|
+
if (operation === 'mutation' && method === 'GET') {
|
|
302
|
+
return [
|
|
303
|
+
'Cannot perform mutations over GET',
|
|
304
|
+
{
|
|
305
|
+
status: 405,
|
|
306
|
+
statusText: 'Method Not Allowed',
|
|
307
|
+
headers: {
|
|
308
|
+
allow: 'POST',
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
];
|
|
312
|
+
}
|
|
313
|
+
if (!('contextValue' in args)) {
|
|
314
|
+
const maybeResOrContext = typeof context === 'function' ? await context(req, args) : context;
|
|
315
|
+
if ((0, common_1.isResponse)(maybeResOrContext))
|
|
316
|
+
return maybeResOrContext;
|
|
317
|
+
args.contextValue = maybeResOrContext;
|
|
318
|
+
}
|
|
319
|
+
const validationErrs = validate(args.schema, args.document);
|
|
320
|
+
if (validationErrs.length) {
|
|
321
|
+
return [
|
|
322
|
+
JSON.stringify({ errors: validationErrs }),
|
|
323
|
+
Object.assign(Object.assign({}, (acceptedMediaType === 'application/json'
|
|
324
|
+
? {
|
|
325
|
+
status: 200,
|
|
326
|
+
statusText: 'OK',
|
|
327
|
+
}
|
|
328
|
+
: {
|
|
329
|
+
status: 400,
|
|
330
|
+
statusText: 'Bad Request',
|
|
331
|
+
})), { headers: {
|
|
332
|
+
'content-type': acceptedMediaType === 'application/json'
|
|
333
|
+
? 'application/json; charset=utf-8'
|
|
334
|
+
: 'application/graphql+json; charset=utf-8',
|
|
335
|
+
} }),
|
|
336
|
+
];
|
|
337
|
+
}
|
|
338
|
+
let result = await execute(args);
|
|
339
|
+
const maybeResOrResult = await (onOperation === null || onOperation === void 0 ? void 0 : onOperation(req, args, result));
|
|
340
|
+
if ((0, common_1.isResponse)(maybeResOrResult))
|
|
341
|
+
return maybeResOrResult;
|
|
342
|
+
else if (maybeResOrResult)
|
|
343
|
+
result = maybeResOrResult;
|
|
344
|
+
return [
|
|
345
|
+
JSON.stringify(result),
|
|
346
|
+
{
|
|
347
|
+
status: 200,
|
|
348
|
+
statusText: 'OK',
|
|
349
|
+
headers: {
|
|
350
|
+
'content-type': acceptedMediaType === 'application/json'
|
|
351
|
+
? 'application/json; charset=utf-8'
|
|
352
|
+
: 'application/graphql+json; charset=utf-8',
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
];
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
exports.createHandler = createHandler;
|