@nexus_js/graphql 0.9.3

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.
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Nexus GraphQL Handler — createGraphQLHandler()
3
+ *
4
+ * Returns a (request: Request, ctx: NexusContext) → Promise<Response> function
5
+ * compatible with the Nexus server `mounts` option.
6
+ *
7
+ * What this handler does
8
+ * ──────────────────────
9
+ * 1. Handles CORS preflight (OPTIONS) so browser clients don't get blocked.
10
+ * Important: Nexus's own CSP/security headers are added by the server on
11
+ * top — we never fight over the same header names.
12
+ *
13
+ * 2. Parses and validates the incoming GraphQL request (GET, POST JSON,
14
+ * POST application/graphql) — no Express, no middleware.
15
+ *
16
+ * 3. Rejects introspection in production mode (configurable).
17
+ *
18
+ * 4. Runs the Shield complexity & depth analysis on the parsed AST *before*
19
+ * calling execute() — saves CPU on malicious queries.
20
+ *
21
+ * 5. Calls graphql.execute() with the context you provide.
22
+ *
23
+ * 6. Applies field masking on the result.
24
+ *
25
+ * 7. Limits per-operation request rate using the built-in rate-limit helpers.
26
+ *
27
+ * Usage (in nexus.config.ts / server startup)
28
+ * ─────────────────────────────────────────────
29
+ * import { createGraphQLHandler } from '@nexus_js/graphql';
30
+ * import { schema } from './graphql/schema.js';
31
+ * import { createBatchLoader } from '@nexus_js/graphql';
32
+ *
33
+ * const gqlHandler = createGraphQLHandler({
34
+ * schema,
35
+ * dev: process.env.NODE_ENV !== 'production',
36
+ * cors: { origins: ['https://app.example.com'], credentials: true },
37
+ * shield: { maxCost: 500, maxDepth: 8 },
38
+ * mask: {
39
+ * 'User.passwordHash': null,
40
+ * 'PaymentCard.cvv': 'REDACTED',
41
+ * },
42
+ * context: (request, nexusCtx) => ({
43
+ * ...nexusCtx,
44
+ * loaders: {
45
+ * user: createBatchLoader(ids => db.users.findMany({ where: { id: { in: ids } } })),
46
+ * },
47
+ * }),
48
+ * });
49
+ *
50
+ * // In createNexusServer():
51
+ * mounts: [{ path: '/graphql', handler: gqlHandler }]
52
+ *
53
+ * CORS notes
54
+ * ──────────
55
+ * - `cors.origins: '*'` + `cors.credentials: true` is rejected (browser security requirement).
56
+ * - For non-CORS requests (same-origin or CLI tools) the `Access-Control-*` headers are not added.
57
+ * - Nexus's hardened security headers are added by the server on top; they don't override CORS.
58
+ */
59
+ import type { GraphQLSchema } from 'graphql';
60
+ import { type ComplexityConfig } from './complexity.js';
61
+ import { type MaskPolicy } from './mask.js';
62
+ export interface MinimalNexusContext {
63
+ request: Request;
64
+ secrets: ReadonlyMap<string, string>;
65
+ locals: Record<string, unknown>;
66
+ [key: string]: unknown;
67
+ }
68
+ export interface CorsConfig {
69
+ /**
70
+ * Allowed origins. Use `'*'` for open access (incompatible with `credentials: true`).
71
+ * Can also be an array of exact origin strings, or a predicate.
72
+ */
73
+ origins: '*' | string[] | ((origin: string) => boolean);
74
+ /**
75
+ * Whether to allow cookies / auth headers.
76
+ * Cannot be combined with `origins: '*'` — the browser will reject this.
77
+ */
78
+ credentials?: boolean;
79
+ /** Extra headers allowed in the request (merged with GraphQL defaults). */
80
+ allowHeaders?: string[];
81
+ /** Cache preflight result for this many seconds. Default: 86400. */
82
+ maxAge?: number;
83
+ }
84
+ export type GraphQLContextFn<Ctx = MinimalNexusContext> = (request: Request, nexusCtx: MinimalNexusContext) => Ctx | Promise<Ctx>;
85
+ export interface RateLimitPerOperation {
86
+ /** Maximum requests per window. */
87
+ max: number;
88
+ /** Window in ms. Default: 60 000 (1 minute). */
89
+ windowMs?: number;
90
+ }
91
+ export interface GraphQLHandlerOptions<Ctx = MinimalNexusContext> {
92
+ /** The compiled GraphQL schema. */
93
+ schema: GraphQLSchema;
94
+ /**
95
+ * `true` in dev mode:
96
+ * - Enables GraphiQL at the endpoint
97
+ * - Allows introspection regardless of `shield.allowIntrospection`
98
+ * - Exposes full error stack traces in responses
99
+ */
100
+ dev?: boolean;
101
+ /**
102
+ * CORS configuration. Omit to disable CORS headers (same-origin only).
103
+ * Required when your API and frontend are on different origins.
104
+ */
105
+ cors?: CorsConfig;
106
+ /**
107
+ * Shield configuration for complexity + depth + introspection.
108
+ * Omit to skip complexity analysis (not recommended for public APIs).
109
+ */
110
+ shield?: ComplexityConfig;
111
+ /**
112
+ * Field masking policy applied to every response.
113
+ * See `MaskPolicy` from `@nexus_js/graphql/mask`.
114
+ */
115
+ mask?: MaskPolicy<Ctx>;
116
+ /**
117
+ * Factory to build the GraphQL execution context per request.
118
+ * Receives the raw `Request` and the Nexus context (`ctx.secrets`, etc.).
119
+ * Return whatever your resolvers expect on `context`.
120
+ */
121
+ context?: GraphQLContextFn<Ctx>;
122
+ /**
123
+ * Max request body size in bytes. Default: 1 MB.
124
+ * Prevents memory exhaustion via oversized query documents.
125
+ */
126
+ maxBodyBytes?: number;
127
+ /**
128
+ * Per-IP rate limiting for the GraphQL endpoint.
129
+ * Applies across all operations (use resolver-level limits for per-operation).
130
+ */
131
+ rateLimit?: RateLimitPerOperation;
132
+ }
133
+ /**
134
+ * Create a Nexus-compatible GraphQL handler.
135
+ *
136
+ * Mount with:
137
+ * ```ts
138
+ * mounts: [{ path: '/graphql', handler: createGraphQLHandler({ schema, ... }) }]
139
+ * ```
140
+ */
141
+ export declare function createGraphQLHandler<Ctx = MinimalNexusContext>(opts: GraphQLHandlerOptions<Ctx>): (request: Request, nexusCtx: MinimalNexusContext) => Promise<Response>;
142
+ //# sourceMappingURL=handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AAEH,OAAO,KAAK,EACV,aAAa,EAEd,MAAM,SAAS,CAAC;AACjB,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAC3E,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAKxD,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,MAAM,EAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAID,MAAM,WAAW,UAAU;IACzB;;;OAGG;IACH,OAAO,EAAE,GAAG,GAAG,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IACxD;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,gBAAgB,CAAC,GAAG,GAAG,mBAAmB,IAAI,CACxD,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,mBAAmB,KAC1B,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;AAExB,MAAM,WAAW,qBAAqB;IACpC,mCAAmC;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,gDAAgD;IAChD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB,CAAC,GAAG,GAAG,mBAAmB;IAC9D,mCAAmC;IACnC,MAAM,EAAE,aAAa,CAAC;IAEtB;;;;;OAKG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;IAEd;;;OAGG;IACH,IAAI,CAAC,EAAE,UAAU,CAAC;IAElB;;;OAGG;IACH,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAE1B;;;OAGG;IACH,IAAI,CAAC,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC;IAEvB;;;;OAIG;IACH,OAAO,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAC;IAEhC;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,SAAS,CAAC,EAAE,qBAAqB,CAAC;CACnC;AA0OD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,GAAG,mBAAmB,EAC5D,IAAI,EAAE,qBAAqB,CAAC,GAAG,CAAC,GAC/B,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,mBAAmB,KAAK,OAAO,CAAC,QAAQ,CAAC,CA6NxE"}
@@ -0,0 +1,420 @@
1
+ /**
2
+ * Nexus GraphQL Handler — createGraphQLHandler()
3
+ *
4
+ * Returns a (request: Request, ctx: NexusContext) → Promise<Response> function
5
+ * compatible with the Nexus server `mounts` option.
6
+ *
7
+ * What this handler does
8
+ * ──────────────────────
9
+ * 1. Handles CORS preflight (OPTIONS) so browser clients don't get blocked.
10
+ * Important: Nexus's own CSP/security headers are added by the server on
11
+ * top — we never fight over the same header names.
12
+ *
13
+ * 2. Parses and validates the incoming GraphQL request (GET, POST JSON,
14
+ * POST application/graphql) — no Express, no middleware.
15
+ *
16
+ * 3. Rejects introspection in production mode (configurable).
17
+ *
18
+ * 4. Runs the Shield complexity & depth analysis on the parsed AST *before*
19
+ * calling execute() — saves CPU on malicious queries.
20
+ *
21
+ * 5. Calls graphql.execute() with the context you provide.
22
+ *
23
+ * 6. Applies field masking on the result.
24
+ *
25
+ * 7. Limits per-operation request rate using the built-in rate-limit helpers.
26
+ *
27
+ * Usage (in nexus.config.ts / server startup)
28
+ * ─────────────────────────────────────────────
29
+ * import { createGraphQLHandler } from '@nexus_js/graphql';
30
+ * import { schema } from './graphql/schema.js';
31
+ * import { createBatchLoader } from '@nexus_js/graphql';
32
+ *
33
+ * const gqlHandler = createGraphQLHandler({
34
+ * schema,
35
+ * dev: process.env.NODE_ENV !== 'production',
36
+ * cors: { origins: ['https://app.example.com'], credentials: true },
37
+ * shield: { maxCost: 500, maxDepth: 8 },
38
+ * mask: {
39
+ * 'User.passwordHash': null,
40
+ * 'PaymentCard.cvv': 'REDACTED',
41
+ * },
42
+ * context: (request, nexusCtx) => ({
43
+ * ...nexusCtx,
44
+ * loaders: {
45
+ * user: createBatchLoader(ids => db.users.findMany({ where: { id: { in: ids } } })),
46
+ * },
47
+ * }),
48
+ * });
49
+ *
50
+ * // In createNexusServer():
51
+ * mounts: [{ path: '/graphql', handler: gqlHandler }]
52
+ *
53
+ * CORS notes
54
+ * ──────────
55
+ * - `cors.origins: '*'` + `cors.credentials: true` is rejected (browser security requirement).
56
+ * - For non-CORS requests (same-origin or CLI tools) the `Access-Control-*` headers are not added.
57
+ * - Nexus's hardened security headers are added by the server on top; they don't override CORS.
58
+ */
59
+ import { analyseComplexity } from './complexity.js';
60
+ import { maskResult } from './mask.js';
61
+ const rateLimitStore = new Map();
62
+ function checkRateLimit(key, max, windowMs) {
63
+ const now = Date.now();
64
+ let win = rateLimitStore.get(key);
65
+ if (!win) {
66
+ win = { timestamps: [] };
67
+ rateLimitStore.set(key, win);
68
+ }
69
+ // Evict timestamps outside the window
70
+ win.timestamps = win.timestamps.filter(t => t > now - windowMs);
71
+ const resetAt = win.timestamps[0] ? win.timestamps[0] + windowMs : now + windowMs;
72
+ if (win.timestamps.length >= max) {
73
+ return { allowed: false, remaining: 0, resetAt };
74
+ }
75
+ win.timestamps.push(now);
76
+ return { allowed: true, remaining: max - win.timestamps.length, resetAt };
77
+ }
78
+ // Prune stale rate-limit entries every 5 min to prevent unbounded growth
79
+ let _pruneTimer = null;
80
+ function ensurePruneTimer() {
81
+ if (_pruneTimer)
82
+ return;
83
+ _pruneTimer = setInterval(() => {
84
+ const cutoff = Date.now() - 300_000;
85
+ for (const [key, win] of rateLimitStore) {
86
+ if (!win.timestamps.length || win.timestamps[win.timestamps.length - 1] < cutoff) {
87
+ rateLimitStore.delete(key);
88
+ }
89
+ }
90
+ }, 300_000);
91
+ if (_pruneTimer && typeof _pruneTimer === 'object' && 'unref' in _pruneTimer) {
92
+ _pruneTimer.unref();
93
+ }
94
+ }
95
+ async function parseGqlRequest(request, maxBodyBytes) {
96
+ const method = request.method.toUpperCase();
97
+ if (method === 'GET') {
98
+ const url = new URL(request.url);
99
+ const query = url.searchParams.get('query');
100
+ if (!query) {
101
+ return { error: 'Missing "query" search parameter.', status: 400 };
102
+ }
103
+ let variables;
104
+ const rawVars = url.searchParams.get('variables');
105
+ if (rawVars) {
106
+ try {
107
+ variables = JSON.parse(rawVars);
108
+ }
109
+ catch {
110
+ return { error: 'Invalid JSON in "variables" parameter.', status: 400 };
111
+ }
112
+ }
113
+ return { query, variables, operationName: url.searchParams.get('operationName') ?? undefined };
114
+ }
115
+ if (method === 'POST') {
116
+ const ct = (request.headers.get('content-type') ?? '').toLowerCase();
117
+ // Body size guard
118
+ const contentLength = parseInt(request.headers.get('content-length') ?? '0', 10);
119
+ if (contentLength > maxBodyBytes) {
120
+ return { error: `Request body exceeds maximum size of ${maxBodyBytes} bytes.`, status: 413 };
121
+ }
122
+ let rawBody;
123
+ try {
124
+ const buf = await request.arrayBuffer();
125
+ if (buf.byteLength > maxBodyBytes) {
126
+ return { error: `Request body exceeds maximum size of ${maxBodyBytes} bytes.`, status: 413 };
127
+ }
128
+ rawBody = new TextDecoder().decode(buf);
129
+ }
130
+ catch {
131
+ return { error: 'Failed to read request body.', status: 400 };
132
+ }
133
+ if (ct.includes('application/graphql')) {
134
+ return { query: rawBody };
135
+ }
136
+ if (ct.includes('application/json')) {
137
+ let body;
138
+ try {
139
+ body = JSON.parse(rawBody);
140
+ }
141
+ catch {
142
+ return { error: 'Invalid JSON body.', status: 400 };
143
+ }
144
+ if (typeof body !== 'object' || body === null) {
145
+ return { error: 'Body must be a JSON object.', status: 400 };
146
+ }
147
+ const b = body;
148
+ if (typeof b['query'] !== 'string') {
149
+ return { error: 'Body must include a "query" string field.', status: 400 };
150
+ }
151
+ let variables;
152
+ if (b['variables'] !== undefined && b['variables'] !== null) {
153
+ if (typeof b['variables'] !== 'object') {
154
+ return { error: '"variables" must be an object.', status: 400 };
155
+ }
156
+ variables = b['variables'];
157
+ }
158
+ return {
159
+ query: b['query'],
160
+ variables,
161
+ operationName: typeof b['operationName'] === 'string' ? b['operationName'] : undefined,
162
+ };
163
+ }
164
+ return { error: `Unsupported Content-Type: ${ct}`, status: 415 };
165
+ }
166
+ return { error: `Method ${method} not allowed. Use GET or POST.`, status: 405 };
167
+ }
168
+ // ── CORS helpers ─────────────────────────────────────────────────────────────
169
+ function resolveOrigin(corsConfig, origin) {
170
+ if (!origin)
171
+ return null;
172
+ const { origins } = corsConfig;
173
+ if (origins === '*') {
174
+ if (corsConfig.credentials)
175
+ return null; // cannot combine * + credentials
176
+ return '*';
177
+ }
178
+ if (Array.isArray(origins)) {
179
+ return origins.includes(origin) ? origin : null;
180
+ }
181
+ if (typeof origins === 'function') {
182
+ return origins(origin) ? origin : null;
183
+ }
184
+ return null;
185
+ }
186
+ const GQL_ALLOW_HEADERS = [
187
+ 'Content-Type',
188
+ 'Authorization',
189
+ 'X-Requested-With',
190
+ 'Accept',
191
+ 'Origin',
192
+ ].join(', ');
193
+ function buildCorsHeaders(corsConfig, origin, extra = []) {
194
+ const allowed = resolveOrigin(corsConfig, origin);
195
+ if (!allowed)
196
+ return {};
197
+ const allowHeaders = [GQL_ALLOW_HEADERS, ...extra].join(', ');
198
+ const headers = {
199
+ 'access-control-allow-origin': allowed,
200
+ 'access-control-allow-methods': 'GET, POST, OPTIONS',
201
+ 'access-control-allow-headers': allowHeaders,
202
+ 'access-control-max-age': String(corsConfig.maxAge ?? 86400),
203
+ };
204
+ if (corsConfig.credentials) {
205
+ headers['access-control-allow-credentials'] = 'true';
206
+ }
207
+ if (allowed !== '*') {
208
+ headers['vary'] = 'Origin';
209
+ }
210
+ return headers;
211
+ }
212
+ // ── GraphiQL HTML ────────────────────────────────────────────────────────────
213
+ function graphiqlHtml(endpoint) {
214
+ return `<!DOCTYPE html>
215
+ <html lang="en"><head>
216
+ <meta charset="UTF-8">
217
+ <meta name="viewport" content="width=device-width,initial-scale=1">
218
+ <title>◆ Nexus GraphiQL</title>
219
+ <link rel="stylesheet" href="https://unpkg.com/graphiql@3/graphiql.min.css">
220
+ <style>
221
+ * { box-sizing: border-box; margin: 0; }
222
+ body { height: 100dvh; display: flex; flex-direction: column; background: #0a0a0f; }
223
+ #graphiql { flex: 1; overflow: hidden; }
224
+ .graphiql-container { background: #0f0f14 !important; }
225
+ </style>
226
+ </head>
227
+ <body>
228
+ <div id="graphiql">Loading GraphiQL…</div>
229
+ <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
230
+ <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
231
+ <script crossorigin src="https://unpkg.com/graphiql@3/graphiql.min.js"></script>
232
+ <script>
233
+ const root = ReactDOM.createRoot(document.getElementById('graphiql'));
234
+ const fetcher = GraphiQL.createFetcher({ url: ${JSON.stringify(endpoint)} });
235
+ root.render(React.createElement(GraphiQL, { fetcher }));
236
+ </script>
237
+ </body></html>`;
238
+ }
239
+ // ── Error response helper ────────────────────────────────────────────────────
240
+ function errorResponse(errors, status, extraHeaders = {}) {
241
+ return new Response(JSON.stringify({ errors: errors.map(e => ({ message: e.message, extensions: e.extensions })) }), {
242
+ status,
243
+ headers: {
244
+ 'content-type': 'application/json; charset=utf-8',
245
+ ...extraHeaders,
246
+ },
247
+ });
248
+ }
249
+ // ── Main factory ─────────────────────────────────────────────────────────────
250
+ /**
251
+ * Create a Nexus-compatible GraphQL handler.
252
+ *
253
+ * Mount with:
254
+ * ```ts
255
+ * mounts: [{ path: '/graphql', handler: createGraphQLHandler({ schema, ... }) }]
256
+ * ```
257
+ */
258
+ export function createGraphQLHandler(opts) {
259
+ const { schema, dev = false, cors: corsOpts, shield: shieldConfig, mask: maskPolicy, context: contextFn, maxBodyBytes = 1_048_576, // 1 MB
260
+ rateLimit: rlConfig, } = opts;
261
+ // Validate CORS config
262
+ if (corsOpts?.credentials && corsOpts.origins === '*') {
263
+ throw new Error('[Nexus GraphQL] `cors.credentials: true` cannot be used with `cors.origins: "*"`. ' +
264
+ 'Browsers will reject this combination. Specify exact allowed origins instead.');
265
+ }
266
+ if (rlConfig)
267
+ ensurePruneTimer();
268
+ return async function graphqlRequestHandler(request, nexusCtx) {
269
+ const method = request.method.toUpperCase();
270
+ const origin = request.headers.get('origin');
271
+ const corsHeaders = corsOpts ? buildCorsHeaders(corsOpts, origin, opts.cors?.allowHeaders) : {};
272
+ // ── 1. CORS preflight ────────────────────────────────────────────────────
273
+ if (method === 'OPTIONS') {
274
+ return new Response(null, {
275
+ status: 204,
276
+ headers: {
277
+ 'content-length': '0',
278
+ ...corsHeaders,
279
+ },
280
+ });
281
+ }
282
+ // ── 2. GraphiQL (dev GET with Accept: text/html) ─────────────────────────
283
+ if (method === 'GET' && dev) {
284
+ const accept = request.headers.get('accept') ?? '';
285
+ const url = new URL(request.url);
286
+ if (accept.includes('text/html') && !url.searchParams.has('query')) {
287
+ return new Response(graphiqlHtml(url.pathname), {
288
+ status: 200,
289
+ headers: { 'content-type': 'text/html; charset=utf-8', ...corsHeaders },
290
+ });
291
+ }
292
+ }
293
+ // ── 3. Rate limiting ─────────────────────────────────────────────────────
294
+ if (rlConfig) {
295
+ const ip = request.headers.get('x-forwarded-for')?.split(',')[0]?.trim()
296
+ ?? request.headers.get('cf-connecting-ip')
297
+ ?? request.headers.get('x-real-ip')
298
+ ?? 'unknown';
299
+ const rl = checkRateLimit(`gql:${ip}`, rlConfig.max, rlConfig.windowMs ?? 60_000);
300
+ if (!rl.allowed) {
301
+ const retryAfter = Math.ceil((rl.resetAt - Date.now()) / 1000);
302
+ return errorResponse([{ message: 'Too many requests. Please retry later.', extensions: { code: 'RATE_LIMITED' } }], 429, {
303
+ ...corsHeaders,
304
+ 'retry-after': String(retryAfter),
305
+ 'x-ratelimit-limit': String(rlConfig.max),
306
+ 'x-ratelimit-remaining': '0',
307
+ 'x-ratelimit-reset': String(Math.ceil(rl.resetAt / 1000)),
308
+ });
309
+ }
310
+ }
311
+ // ── 4. Parse GraphQL request ─────────────────────────────────────────────
312
+ const parsed = await parseGqlRequest(request, maxBodyBytes);
313
+ if ('error' in parsed) {
314
+ return errorResponse([{ message: parsed.error, extensions: { code: 'BAD_REQUEST' } }], parsed.status, corsHeaders);
315
+ }
316
+ // ── 5. Parse + validate GraphQL document ─────────────────────────────────
317
+ // Lazy-import graphql to keep it as a true peer dep
318
+ const gql = await import('graphql');
319
+ let document;
320
+ try {
321
+ document = gql.parse(parsed.query);
322
+ }
323
+ catch (err) {
324
+ return errorResponse([{ message: err.message, extensions: { code: 'GRAPHQL_PARSE_FAILED' } }], 400, corsHeaders);
325
+ }
326
+ const validationErrors = gql.validate(schema, document);
327
+ if (validationErrors.length > 0) {
328
+ return errorResponse(validationErrors.map(e => ({
329
+ message: e.message,
330
+ extensions: { code: 'GRAPHQL_VALIDATION_FAILED' },
331
+ })), 400, corsHeaders);
332
+ }
333
+ // ── 6. Shield: complexity + depth analysis ───────────────────────────────
334
+ if (shieldConfig) {
335
+ const shieldWithDev = {
336
+ ...shieldConfig,
337
+ // Always allow introspection in dev
338
+ allowIntrospection: dev ? true : (shieldConfig.allowIntrospection ?? false),
339
+ };
340
+ const { errors: complexityErrors } = analyseComplexity(document, schema, shieldWithDev);
341
+ if (complexityErrors.length > 0) {
342
+ return errorResponse(complexityErrors, 400, corsHeaders);
343
+ }
344
+ }
345
+ else if (!dev) {
346
+ // Even without explicit shield config: block introspection in production
347
+ for (const def of document.definitions) {
348
+ if (def.kind === 'OperationDefinition') {
349
+ for (const sel of def.selectionSet.selections) {
350
+ if (sel.kind === 'Field' && sel.name.value === '__schema') {
351
+ return errorResponse([{ message: 'Introspection is disabled.', extensions: { code: 'INTROSPECTION_DISABLED' } }], 403, corsHeaders);
352
+ }
353
+ }
354
+ }
355
+ }
356
+ }
357
+ // ── 7. Build execution context ───────────────────────────────────────────
358
+ let execContext;
359
+ if (contextFn) {
360
+ try {
361
+ execContext = await contextFn(request, nexusCtx);
362
+ }
363
+ catch (err) {
364
+ return errorResponse([{ message: 'Failed to build request context.', extensions: { code: 'CONTEXT_ERROR' } }], 500, corsHeaders);
365
+ }
366
+ }
367
+ else {
368
+ execContext = nexusCtx;
369
+ }
370
+ // ── 8. Execute ───────────────────────────────────────────────────────────
371
+ let rawResult;
372
+ try {
373
+ const execResult = await gql.execute({
374
+ schema,
375
+ document,
376
+ contextValue: execContext,
377
+ variableValues: parsed.variables,
378
+ operationName: parsed.operationName,
379
+ });
380
+ const errs = execResult.errors
381
+ ? execResult.errors.map(e => ({ message: e.message, extensions: e.extensions, path: e.path, locations: e.locations }))
382
+ : undefined;
383
+ rawResult = {
384
+ data: execResult.data,
385
+ ...(errs ? { errors: errs } : {}),
386
+ ...(execResult.extensions ? { extensions: execResult.extensions } : {}),
387
+ };
388
+ }
389
+ catch (err) {
390
+ const msg = dev
391
+ ? `Execution error: ${err.message}`
392
+ : 'Internal server error';
393
+ return errorResponse([{ message: msg, extensions: { code: 'INTERNAL_SERVER_ERROR' } }], 500, corsHeaders);
394
+ }
395
+ // ── 9. Field masking ─────────────────────────────────────────────────────
396
+ const finalResult = maskPolicy
397
+ ? maskResult(rawResult, maskPolicy, execContext)
398
+ : rawResult;
399
+ // Strip stack traces in production
400
+ if (!dev && finalResult.errors) {
401
+ finalResult.errors = finalResult.errors.map(e => ({
402
+ message: String(e['message'] ?? ''),
403
+ ...(e['extensions'] ? { extensions: e['extensions'] } : {}),
404
+ ...(e['path'] ? { path: e['path'] } : {}),
405
+ ...(e['locations'] ? { locations: e['locations'] } : {}),
406
+ }));
407
+ }
408
+ const hasErrors = Array.isArray(finalResult.errors) && finalResult.errors.length > 0;
409
+ const httpStatus = hasErrors && !finalResult.data ? 400 : 200;
410
+ return new Response(JSON.stringify(finalResult), {
411
+ status: httpStatus,
412
+ headers: {
413
+ 'content-type': 'application/json; charset=utf-8',
414
+ 'cache-control': 'no-store',
415
+ ...corsHeaders,
416
+ },
417
+ });
418
+ };
419
+ }
420
+ //# sourceMappingURL=handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @nexus_js/graphql — GraphQL integration for Nexus.js
3
+ *
4
+ * Provides a security-first GraphQL adapter that integrates with the Nexus
5
+ * server pipeline without Express, without CORS conflicts, and without
6
+ * external runtime dependencies beyond `graphql` itself.
7
+ *
8
+ * Quick-start
9
+ * ───────────
10
+ * ```ts
11
+ * // nexus.config.ts or server startup
12
+ * import { createGraphQLHandler, createBatchLoader, createJwtService } from '@nexus_js/graphql';
13
+ * import { nexusVault } from '@nexus_js/security';
14
+ * import { schema } from './graphql/schema.js';
15
+ *
16
+ * const jwtSvc = createJwtService(nexusVault, { vaultKey: 'JWT_SECRET', issuer: 'my-app' });
17
+ *
18
+ * const gqlHandler = createGraphQLHandler({
19
+ * schema,
20
+ * dev: process.env.NODE_ENV !== 'production',
21
+ * cors: { origins: ['https://app.example.com'], credentials: true },
22
+ * shield: { maxCost: 500, maxDepth: 8, allowIntrospection: false },
23
+ * mask: { 'User.passwordHash': null, 'PaymentCard.cvv': 'REDACTED' },
24
+ * context: (req, ctx) => ({
25
+ * ...ctx,
26
+ * user: jwtSvc.verify(req.headers.get('authorization')?.replace('Bearer ', '') ?? ''),
27
+ * loaders: { user: createBatchLoader(ids => db.users.findMany(ids)) },
28
+ * }),
29
+ * });
30
+ *
31
+ * // Add to createNexusServer:
32
+ * mounts: [{ path: '/graphql', handler: gqlHandler }]
33
+ * ```
34
+ *
35
+ * Architecture
36
+ * ────────────
37
+ * Handler pipeline per request:
38
+ * 1. CORS preflight (OPTIONS) → 204 with Access-Control headers
39
+ * 2. GraphiQL HTML (dev GET with Accept: text/html)
40
+ * 3. Rate limiting (sliding window, per IP)
41
+ * 4. Parse GET/POST JSON/application/graphql body
42
+ * 5. graphql.parse() + graphql.validate()
43
+ * 6. Shield: complexity + depth + introspection gate
44
+ * 7. Context factory call
45
+ * 8. graphql.execute()
46
+ * 9. Field masking
47
+ * 10. JSON response
48
+ */
49
+ export { createGraphQLHandler, type GraphQLHandlerOptions, type CorsConfig, type GraphQLContextFn, type MinimalNexusContext, type RateLimitPerOperation, } from './handler.js';
50
+ export { analyseComplexity, type ComplexityConfig, type ComplexityResult, } from './complexity.js';
51
+ export { maskResult, allowWhen, redactUnless, type MaskPolicy, type MaskFn, type GraphQLExecutionResult, } from './mask.js';
52
+ export { createJwtService, signJwt, verifyJwt, type JwtService, type JwtServiceOptions, type JwtPayload, } from './jwt.js';
53
+ export { BatchLoader, createBatchLoader, createLoaderRegistry, type BatchFn, type BatchLoaderOptions, } from './dataloader.js';
54
+ export { createRemoteExecutor, createRemoteExecutorWithSchema, type RemoteExecutorOptions, type RemoteExecutionContext, type RemoteExecutionResult, } from './remote-executor.js';
55
+ export { stitchSchemas, createGatewayResolver, type SubschemaConfig, type StitchSchemasOptions, } from './stitching.js';
56
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAGH,OAAO,EACL,oBAAoB,EACpB,KAAK,qBAAqB,EAC1B,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,GAC3B,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,GACtB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,UAAU,EACV,SAAS,EACT,YAAY,EACZ,KAAK,UAAU,EACf,KAAK,MAAM,EACX,KAAK,sBAAsB,GAC5B,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,gBAAgB,EAChB,OAAO,EACP,SAAS,EACT,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,UAAU,GAChB,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,OAAO,EACZ,KAAK,kBAAkB,GACxB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,oBAAoB,EACpB,8BAA8B,EAC9B,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,GAC3B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,KAAK,eAAe,EACpB,KAAK,oBAAoB,GAC1B,MAAM,gBAAgB,CAAC"}