Skip to content

Commit 3a593f8

Browse files
committed
feat(handler): Request may contain a context value
1 parent 4bc71ee commit 3a593f8

File tree

7 files changed

+85
-36
lines changed

7 files changed

+85
-36
lines changed

docs/README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,14 @@ ___
125125

126126
### Handler
127127

128-
Ƭ **Handler**<`RawRequest`\>: (`req`: [`Request`](interfaces/Request.md)<`RawRequest`\>) => `Promise`<[`Response`](README.md#response)\>
128+
Ƭ **Handler**<`RawRequest`, `Context`\>: (`req`: [`Request`](interfaces/Request.md)<`RawRequest`, `Context`\>) => `Promise`<[`Response`](README.md#response)\>
129129

130130
#### Type parameters
131131

132132
| Name | Type |
133133
| :------ | :------ |
134134
| `RawRequest` | `unknown` |
135+
| `Context` | `unknown` |
135136

136137
#### Type declaration
137138

@@ -148,7 +149,7 @@ considered internal errors and you should take care of them accordingly.
148149

149150
| Name | Type |
150151
| :------ | :------ |
151-
| `req` | [`Request`](interfaces/Request.md)<`RawRequest`\> |
152+
| `req` | [`Request`](interfaces/Request.md)<`RawRequest`, `Context`\> |
152153

153154
##### Returns
154155

@@ -158,7 +159,7 @@ ___
158159

159160
### createHandler
160161

161-
**createHandler**<`RawRequest`\>(`options`): [`Handler`](README.md#handler)<`RawRequest`\>
162+
**createHandler**<`RawRequest`, `Context`\>(`options`): [`Handler`](README.md#handler)<`RawRequest`, `Context`\>
162163

163164
Makes a GraphQL over HTTP Protocol compliant server handler. The handler can
164165
be used with your favourite server library.
@@ -217,16 +218,17 @@ console.log('Listening to port 4000');
217218
| Name | Type |
218219
| :------ | :------ |
219220
| `RawRequest` | `unknown` |
221+
| `Context` | `unknown` |
220222

221223
#### Parameters
222224

223225
| Name | Type |
224226
| :------ | :------ |
225-
| `options` | [`HandlerOptions`](interfaces/HandlerOptions.md)<`RawRequest`\> |
227+
| `options` | [`HandlerOptions`](interfaces/HandlerOptions.md)<`RawRequest`, `Context`\> |
226228

227229
#### Returns
228230

229-
[`Handler`](README.md#handler)<`RawRequest`\>
231+
[`Handler`](README.md#handler)<`RawRequest`, `Context`\>
230232

231233
___
232234

docs/interfaces/HandlerOptions.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
[graphql-http](../README.md) / HandlerOptions
22

3-
# Interface: HandlerOptions<RawRequest\>
3+
# Interface: HandlerOptions<RawRequest, Context\>
44

55
## Type parameters
66

77
| Name | Type |
88
| :------ | :------ |
99
| `RawRequest` | `unknown` |
10+
| `Context` | `unknown` |
1011

1112
## Table of contents
1213

@@ -25,7 +26,7 @@
2526

2627
### context
2728

28-
`Optional` **context**: [`ExecutionContext`](../README.md#executioncontext) \| (`req`: [`Request`](Request.md)<`RawRequest`\>, `args`: `ExecutionArgs`) => [`Response`](../README.md#response) \| [`ExecutionContext`](../README.md#executioncontext) \| `Promise`<[`Response`](../README.md#response) \| [`ExecutionContext`](../README.md#executioncontext)\>
29+
`Optional` **context**: [`ExecutionContext`](../README.md#executioncontext) \| (`req`: [`Request`](Request.md)<`RawRequest`, `Context`\>, `args`: `ExecutionArgs`) => [`Response`](../README.md#response) \| [`ExecutionContext`](../README.md#executioncontext) \| `Promise`<[`Response`](../README.md#response) \| [`ExecutionContext`](../README.md#executioncontext)\>
2930

3031
A value which is provided to every resolver and holds
3132
important contextual information like the currently
@@ -89,7 +90,7 @@ ___
8990

9091
### onOperation
9192

92-
`Optional` **onOperation**: (`req`: [`Request`](Request.md)<`RawRequest`\>, `args`: `ExecutionArgs`, `result`: `ExecutionResult`<`ObjMap`<`unknown`\>, `ObjMap`<`unknown`\>\>) => `void` \| `ExecutionResult`<`ObjMap`<`unknown`\>, `ObjMap`<`unknown`\>\> \| [`Response`](../README.md#response) \| `Promise`<`void` \| `ExecutionResult`<`ObjMap`<`unknown`\>, `ObjMap`<`unknown`\>\> \| [`Response`](../README.md#response)\>
93+
`Optional` **onOperation**: (`req`: [`Request`](Request.md)<`RawRequest`, `Context`\>, `args`: `ExecutionArgs`, `result`: `ExecutionResult`<`ObjMap`<`unknown`\>, `ObjMap`<`unknown`\>\>) => `void` \| `ExecutionResult`<`ObjMap`<`unknown`\>, `ObjMap`<`unknown`\>\> \| [`Response`](../README.md#response) \| `Promise`<`void` \| `ExecutionResult`<`ObjMap`<`unknown`\>, `ObjMap`<`unknown`\>\> \| [`Response`](../README.md#response)\>
9394

9495
#### Type declaration
9596

@@ -111,7 +112,7 @@ further execution.
111112

112113
| Name | Type |
113114
| :------ | :------ |
114-
| `req` | [`Request`](Request.md)<`RawRequest`\> |
115+
| `req` | [`Request`](Request.md)<`RawRequest`, `Context`\> |
115116
| `args` | `ExecutionArgs` |
116117
| `result` | `ExecutionResult`<`ObjMap`<`unknown`\>, `ObjMap`<`unknown`\>\> |
117118

@@ -123,7 +124,7 @@ ___
123124

124125
### onSubscribe
125126

126-
`Optional` **onSubscribe**: (`req`: [`Request`](Request.md)<`RawRequest`\>, `params`: [`RequestParams`](RequestParams.md)) => `void` \| readonly `GraphQLError`[] \| `ExecutionResult`<`ObjMap`<`unknown`\>, `ObjMap`<`unknown`\>\> \| [`Response`](../README.md#response) \| `ExecutionArgs` \| `Promise`<`void` \| readonly `GraphQLError`[] \| `ExecutionResult`<`ObjMap`<`unknown`\>, `ObjMap`<`unknown`\>\> \| [`Response`](../README.md#response) \| `ExecutionArgs`\>
127+
`Optional` **onSubscribe**: (`req`: [`Request`](Request.md)<`RawRequest`, `Context`\>, `params`: [`RequestParams`](RequestParams.md)) => `void` \| readonly `GraphQLError`[] \| `ExecutionResult`<`ObjMap`<`unknown`\>, `ObjMap`<`unknown`\>\> \| [`Response`](../README.md#response) \| `ExecutionArgs` \| `Promise`<`void` \| readonly `GraphQLError`[] \| `ExecutionResult`<`ObjMap`<`unknown`\>, `ObjMap`<`unknown`\>\> \| [`Response`](../README.md#response) \| `ExecutionArgs`\>
127128

128129
#### Type declaration
129130

@@ -160,7 +161,7 @@ further execution.
160161

161162
| Name | Type |
162163
| :------ | :------ |
163-
| `req` | [`Request`](Request.md)<`RawRequest`\> |
164+
| `req` | [`Request`](Request.md)<`RawRequest`, `Context`\> |
164165
| `params` | [`RequestParams`](RequestParams.md) |
165166

166167
##### Returns
@@ -195,7 +196,7 @@ ___
195196

196197
### schema
197198

198-
`Optional` **schema**: `GraphQLSchema` \| (`req`: [`Request`](Request.md)<`RawRequest`\>, `args`: `Omit`<`ExecutionArgs`, ``"schema"``\>) => [`Response`](../README.md#response) \| `GraphQLSchema` \| `Promise`<[`Response`](../README.md#response) \| `GraphQLSchema`\>
199+
`Optional` **schema**: `GraphQLSchema` \| (`req`: [`Request`](Request.md)<`RawRequest`, `Context`\>, `args`: `Omit`<`ExecutionArgs`, ``"schema"``\>) => [`Response`](../README.md#response) \| `GraphQLSchema` \| `Promise`<[`Response`](../README.md#response) \| `GraphQLSchema`\>
199200

200201
The GraphQL schema on which the operations will
201202
be executed and validated against.

docs/interfaces/Request.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[graphql-http](../README.md) / Request
22

3-
# Interface: Request<RawRequest\>
3+
# Interface: Request<RawRequest, Context\>
44

55
Server agnostic request interface containing the raw request
66
which is server dependant.
@@ -10,12 +10,14 @@ which is server dependant.
1010
| Name |
1111
| :------ |
1212
| `RawRequest` |
13+
| `Context` |
1314

1415
## Table of contents
1516

1617
### Properties
1718

1819
- [body](Request.md#body)
20+
- [context](Request.md#context)
1921
- [headers](Request.md#headers)
2022
- [method](Request.md#method)
2123
- [raw](Request.md#raw)
@@ -29,6 +31,14 @@ which is server dependant.
2931

3032
___
3133

34+
### context
35+
36+
`Readonly` **context**: `Context`
37+
38+
Context value about the incoming request, you're free to pass any information here.
39+
40+
___
41+
3242
### headers
3343

3444
`Readonly` **headers**: [`RequestHeaders`](RequestHeaders.md)

src/__tests__/handler.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { jest } from '@jest/globals';
22
import { GraphQLError } from 'graphql';
33
import fetch from 'node-fetch';
4+
import { Request } from '../common';
45
import { startTServer } from './utils/tserver';
56

67
it.each(['schema', 'context', 'onSubscribe', 'onOperation'])(
@@ -51,3 +52,25 @@ it('should respond with result returned from onSubscribe', async () => {
5152
expect(res.json()).resolves.toEqual({ data: { __typename: 'Query' } });
5253
expect(onOperationFn).not.toBeCalled(); // early result, operation did not happen
5354
});
55+
56+
it.each(['schema', 'context', 'onSubscribe', 'onOperation'])(
57+
'should provide the request context to %s',
58+
async (option) => {
59+
const optionFn = jest.fn<(req: Request<unknown, unknown>) => void>();
60+
61+
const context = {};
62+
const server = startTServer({
63+
changeRequest: (req) => ({
64+
...req,
65+
context,
66+
}),
67+
[option]: optionFn,
68+
});
69+
70+
const url = new URL(server.url);
71+
url.searchParams.set('query', '{ __typename }');
72+
await fetch(url.toString());
73+
74+
expect(optionFn.mock.calls[0][0]?.context).toBe(context);
75+
},
76+
);

src/__tests__/utils/tserver.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createHandler, HandlerOptions } from '../../handler';
2+
import { Request } from '../../common';
23
import http from 'http';
34
import net from 'net';
45
import { Response } from 'node-fetch';
@@ -19,11 +20,16 @@ export interface TServer {
1920
dispose: Dispose;
2021
}
2122
export function startTServer(
22-
options: HandlerOptions<http.IncomingMessage> = {},
23+
options: HandlerOptions<http.IncomingMessage> & {
24+
changeRequest?: (
25+
req: Request<http.IncomingMessage, unknown>,
26+
) => Request<http.IncomingMessage, unknown>;
27+
} = {},
2328
): TServer {
29+
const { changeRequest = (req) => req, ...handlerOptions } = options;
2430
const handle = createHandler({
2531
schema,
26-
...options,
32+
...handlerOptions,
2733
});
2834
const [url, dispose] = startDisposableServer(
2935
http.createServer(async (req, res) => {
@@ -34,17 +40,20 @@ export function startTServer(
3440
if (!req.method) {
3541
throw new Error('Missing request method');
3642
}
37-
const [body, init] = await handle({
38-
url: req.url,
39-
method: req.method,
40-
headers: req.headers,
41-
body: await new Promise<string>((resolve) => {
42-
let body = '';
43-
req.on('data', (chunk) => (body += chunk));
44-
req.on('end', () => resolve(body));
43+
const [body, init] = await handle(
44+
changeRequest({
45+
url: req.url,
46+
method: req.method,
47+
headers: req.headers,
48+
body: await new Promise<string>((resolve) => {
49+
let body = '';
50+
req.on('data', (chunk) => (body += chunk));
51+
req.on('end', () => resolve(body));
52+
}),
53+
raw: req,
54+
context: null,
4555
}),
46-
raw: req,
47-
});
56+
);
4857
res.writeHead(init.status, init.statusText, init.headers).end(body);
4958
} catch (err) {
5059
if (err instanceof Error) {

src/common.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export interface RequestHeaders {
3030
*
3131
* @category Common
3232
*/
33-
export interface Request<RawRequest> {
33+
export interface Request<RawRequest, Context> {
3434
readonly method: string;
3535
readonly url: string;
3636
readonly headers: RequestHeaders;
@@ -42,6 +42,10 @@ export interface Request<RawRequest> {
4242
* `http.IncomingMessage` when just using Node with `http.createServer`.
4343
*/
4444
readonly raw: RawRequest;
45+
/**
46+
* Context value about the incoming request, you're free to pass any information here.
47+
*/
48+
readonly context: Context;
4549
}
4650

4751
/**

src/handler.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export type ExecutionContext =
4040
| null;
4141

4242
/** @category Server */
43-
export interface HandlerOptions<RawRequest = unknown> {
43+
export interface HandlerOptions<RawRequest = unknown, Context = unknown> {
4444
/**
4545
* The GraphQL schema on which the operations will
4646
* be executed and validated against.
@@ -60,7 +60,7 @@ export interface HandlerOptions<RawRequest = unknown> {
6060
schema?:
6161
| GraphQLSchema
6262
| ((
63-
req: Request<RawRequest>,
63+
req: Request<RawRequest, Context>,
6464
args: Omit<ExecutionArgs, 'schema'>,
6565
) => Promise<GraphQLSchema | Response> | GraphQLSchema | Response);
6666
/**
@@ -71,7 +71,7 @@ export interface HandlerOptions<RawRequest = unknown> {
7171
context?:
7272
| ExecutionContext
7373
| ((
74-
req: Request<RawRequest>,
74+
req: Request<RawRequest, Context>,
7575
args: ExecutionArgs,
7676
) => Promise<ExecutionContext | Response> | ExecutionContext | Response);
7777
/**
@@ -123,7 +123,7 @@ export interface HandlerOptions<RawRequest = unknown> {
123123
* further execution.
124124
*/
125125
onSubscribe?: (
126-
req: Request<RawRequest>,
126+
req: Request<RawRequest, Context>,
127127
params: RequestParams,
128128
) =>
129129
| Promise<
@@ -152,7 +152,7 @@ export interface HandlerOptions<RawRequest = unknown> {
152152
* further execution.
153153
*/
154154
onOperation?: (
155-
req: Request<RawRequest>,
155+
req: Request<RawRequest, Context>,
156156
args: ExecutionArgs,
157157
result: ExecutionResult,
158158
) =>
@@ -172,8 +172,8 @@ export interface HandlerOptions<RawRequest = unknown> {
172172
*
173173
* @category Server
174174
*/
175-
export type Handler<RawRequest = unknown> = (
176-
req: Request<RawRequest>,
175+
export type Handler<RawRequest = unknown, Context = unknown> = (
176+
req: Request<RawRequest, Context>,
177177
) => Promise<Response>;
178178

179179
/**
@@ -231,9 +231,9 @@ export type Handler<RawRequest = unknown> = (
231231
*
232232
* @category Server
233233
*/
234-
export function createHandler<RawRequest = unknown>(
235-
options: HandlerOptions<RawRequest>,
236-
): Handler<RawRequest> {
234+
export function createHandler<RawRequest = unknown, Context = unknown>(
235+
options: HandlerOptions<RawRequest, Context>,
236+
): Handler<RawRequest, Context> {
237237
const {
238238
schema,
239239
context,

0 commit comments

Comments
 (0)