@whatwg-node/server 0.5.9-alpha-20230130162825-1e02017 → 0.5.9
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/README.md +97 -30
- package/index.js +12 -4
- package/index.mjs +12 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# WHATWG Node Generic Server Adapter
|
|
2
2
|
|
|
3
|
-
`@whatwg-node/server` helps you to create a generic server implementation by using WHATWG Fetch API
|
|
3
|
+
`@whatwg-node/server` helps you to create a generic server implementation by using WHATWG Fetch API
|
|
4
|
+
for Node.js, AWS Lambda, Cloudflare Workers, Deno, Express, Fastify, Koa, Next.js and Sveltekit.
|
|
4
5
|
|
|
5
|
-
Once you create an adapter with `createServerAdapter`, you don't need to install any other platform
|
|
6
|
+
Once you create an adapter with `createServerAdapter`, you don't need to install any other platform
|
|
7
|
+
specific package since the generic adapter will handle it automatically.
|
|
6
8
|
|
|
7
9
|
## How to start
|
|
8
10
|
|
|
@@ -26,8 +28,8 @@ You can use your server adapter with the following integrations:
|
|
|
26
28
|
[Node.js](https://nodejs.org/api/http.html) is the most popular server side JavaScript runtime.
|
|
27
29
|
|
|
28
30
|
```ts
|
|
29
|
-
import myServerAdapter from './myServerAdapter'
|
|
30
31
|
import { createServer } from 'http'
|
|
32
|
+
import myServerAdapter from './myServerAdapter'
|
|
31
33
|
|
|
32
34
|
// You can create your Node server instance by using our adapter
|
|
33
35
|
const nodeServer = createServer(myServerAdapter)
|
|
@@ -37,21 +39,71 @@ nodeServer.listen(4000)
|
|
|
37
39
|
|
|
38
40
|
### AWS Lambda
|
|
39
41
|
|
|
40
|
-
AWS Lambda is a serverless computing platform that makes it easy to build applications that run on
|
|
42
|
+
AWS Lambda is a serverless computing platform that makes it easy to build applications that run on
|
|
43
|
+
the AWS cloud. Our adaoter is platform agnostic so they can fit together easily. In order to reduce
|
|
44
|
+
the boilerplate we prefer to use
|
|
45
|
+
[Serverless Express from Vendia](https://github.com/vendia/serverless-express).
|
|
41
46
|
|
|
42
47
|
```ts
|
|
43
|
-
import
|
|
48
|
+
import { APIGatewayEvent, APIGatewayProxyResult, Context } from 'aws-lambda'
|
|
44
49
|
import type { Handler } from '@aws-cdk/aws-lambda'
|
|
45
|
-
import
|
|
50
|
+
import myServerAdapter from './myServerAdapter'
|
|
46
51
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
interface ServerContext {
|
|
53
|
+
event: APIGatewayEvent
|
|
54
|
+
lambdaContext: Context
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function handler(
|
|
58
|
+
event: APIGatewayEvent,
|
|
59
|
+
lambdaContext: Context
|
|
60
|
+
): Promise<APIGatewayProxyResult> {
|
|
61
|
+
const url = new URL(event.path, 'http://localhost')
|
|
62
|
+
if (event.queryStringParameters != null) {
|
|
63
|
+
for (const name in event.queryStringParameters) {
|
|
64
|
+
const value = event.queryStringParameters[name]
|
|
65
|
+
if (value != null) {
|
|
66
|
+
url.searchParams.set(name, value)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const response = await myServerAdapter.fetch(
|
|
72
|
+
url,
|
|
73
|
+
{
|
|
74
|
+
// For v1.0 you should use event.httpMethod
|
|
75
|
+
method: event.requestContext.http.method,
|
|
76
|
+
headers: event.headers as HeadersInit,
|
|
77
|
+
body: event.body
|
|
78
|
+
? Buffer.from(event.body, event.isBase64Encoded ? 'base64' : 'utf8')
|
|
79
|
+
: undefined
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
event,
|
|
83
|
+
lambdaContext
|
|
84
|
+
}
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
const responseHeaders: Record<string, string> = {}
|
|
88
|
+
|
|
89
|
+
response.headers.forEach((value, name) => {
|
|
90
|
+
responseHeaders[name] = value
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
statusCode: response.status,
|
|
95
|
+
headers: responseHeaders,
|
|
96
|
+
body: await response.text(),
|
|
97
|
+
isBase64Encoded: false
|
|
98
|
+
}
|
|
99
|
+
}
|
|
50
100
|
```
|
|
51
101
|
|
|
52
102
|
### Cloudflare Workers
|
|
53
103
|
|
|
54
|
-
Cloudflare Workers provides a serverless execution environment that allows you to create entirely
|
|
104
|
+
Cloudflare Workers provides a serverless execution environment that allows you to create entirely
|
|
105
|
+
new applications or augment existing ones without configuring or maintaining infrastructure. It uses
|
|
106
|
+
Fetch API already so we can use our adapter as an event listener like below;
|
|
55
107
|
|
|
56
108
|
```ts
|
|
57
109
|
import myServerAdapter from './myServerAdapter'
|
|
@@ -65,18 +117,20 @@ self.addEventListener('fetch', myServerAdapter)
|
|
|
65
117
|
You can use our adapter as a Deno request handler like below;
|
|
66
118
|
|
|
67
119
|
```ts
|
|
68
|
-
import { serve } from 'https://deno.land/std@0.
|
|
120
|
+
import { serve } from 'https://deno.land/std@0.157.0/http/server.ts'
|
|
69
121
|
import myServerAdapter from './myServerAdapter'
|
|
70
122
|
|
|
71
123
|
serve(myServerAdapter, {
|
|
72
|
-
|
|
73
|
-
|
|
124
|
+
onListen({ hostname, port }) {
|
|
125
|
+
console.log(`Listening on http://${hostname}:${port}/graphql`)
|
|
126
|
+
}
|
|
74
127
|
})
|
|
75
128
|
```
|
|
76
129
|
|
|
77
130
|
### Express
|
|
78
131
|
|
|
79
|
-
[Express is the most popular web framework for Node.js.](https://expressjs.com/) It is a minimalist
|
|
132
|
+
[Express is the most popular web framework for Node.js.](https://expressjs.com/) It is a minimalist
|
|
133
|
+
framework that provides a robust set of features to handle HTTP on Node.js applications.
|
|
80
134
|
|
|
81
135
|
You can easily integrate your adapter into your Express application with a few lines of code.
|
|
82
136
|
|
|
@@ -96,14 +150,15 @@ app.listen(4000, () => {
|
|
|
96
150
|
|
|
97
151
|
### Fastify
|
|
98
152
|
|
|
99
|
-
[Fastify is one of the popular HTTP server frameworks for Node.js.](https://www.fastify.io/). You
|
|
153
|
+
[Fastify is one of the popular HTTP server frameworks for Node.js.](https://www.fastify.io/). You
|
|
154
|
+
can use your adapter easily with Fastify.
|
|
100
155
|
|
|
101
156
|
So you can benefit from the powerful plugins of Fastify ecosystem.
|
|
102
157
|
[See the ecosystem](https://www.fastify.io/docs/latest/Guides/Ecosystem/)
|
|
103
158
|
|
|
104
159
|
```ts
|
|
160
|
+
import fastify, { FastifyReply, FastifyRequest } from 'fastify'
|
|
105
161
|
import myServerAdapter from './myServerAdapter'
|
|
106
|
-
import fastify, { FastifyRequest, FastifyReply } from 'fastify'
|
|
107
162
|
|
|
108
163
|
// This is the fastify instance you have created
|
|
109
164
|
const app = fastify({ logger: true })
|
|
@@ -127,7 +182,8 @@ app.route({
|
|
|
127
182
|
|
|
128
183
|
reply.status(response.status)
|
|
129
184
|
|
|
130
|
-
|
|
185
|
+
// Fastify doesn't accept `null` as a response body
|
|
186
|
+
reply.send(response.body || undefined)
|
|
131
187
|
|
|
132
188
|
return reply
|
|
133
189
|
}
|
|
@@ -138,7 +194,9 @@ app.listen(4000)
|
|
|
138
194
|
|
|
139
195
|
### Koa
|
|
140
196
|
|
|
141
|
-
[Koa is another Node.js server framework designed by the team behind Express, which aims to be a smaller, more expressive.](https://koajs.com/)
|
|
197
|
+
[Koa is another Node.js server framework designed by the team behind Express, which aims to be a smaller, more expressive.](https://koajs.com/)
|
|
198
|
+
You can add your adapter to your Koa application with a few lines of code then
|
|
199
|
+
[benefit middlewares written for Koa.](https://github.com/koajs/koa/wiki)
|
|
142
200
|
|
|
143
201
|
```ts
|
|
144
202
|
import Koa from 'koa'
|
|
@@ -167,13 +225,14 @@ app.listen(4000, () => {
|
|
|
167
225
|
|
|
168
226
|
### Next.js
|
|
169
227
|
|
|
170
|
-
[Next.js](https://nextjs.org/) is a web framework that allows you to build websites very quickly and
|
|
228
|
+
[Next.js](https://nextjs.org/) is a web framework that allows you to build websites very quickly and
|
|
229
|
+
our new server adapter can be integrated with Next.js easily as an API Route.
|
|
171
230
|
|
|
172
231
|
```ts
|
|
173
232
|
// pages/api/myEndpoint.ts
|
|
174
233
|
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
|
175
|
-
import myServerAdapter from './myServerAdapter'
|
|
176
234
|
import type { NextApiRequest, NextApiResponse } from 'next'
|
|
235
|
+
import myServerAdapter from './myServerAdapter'
|
|
177
236
|
|
|
178
237
|
export const config = {
|
|
179
238
|
api: {
|
|
@@ -187,7 +246,8 @@ export default myServerAdapter
|
|
|
187
246
|
|
|
188
247
|
### SvelteKit
|
|
189
248
|
|
|
190
|
-
[SvelteKit](https://kit.svelte.dev/) is the fastest way to build svelte apps. It is very simple, and
|
|
249
|
+
[SvelteKit](https://kit.svelte.dev/) is the fastest way to build svelte apps. It is very simple, and
|
|
250
|
+
let you build frontend & backend in a single place
|
|
191
251
|
|
|
192
252
|
```ts
|
|
193
253
|
import myServerAdapter from './myServerAdapter'
|
|
@@ -197,8 +257,8 @@ export { myServerAdapter as get, myServerAdapter as post }
|
|
|
197
257
|
|
|
198
258
|
### Bun
|
|
199
259
|
|
|
200
|
-
[Bun](https://bun.sh/) is a modern JavaScript runtime like Node or Deno, and it supports Fetch API
|
|
201
|
-
So the configuration is really simple like any other JS runtime;
|
|
260
|
+
[Bun](https://bun.sh/) is a modern JavaScript runtime like Node or Deno, and it supports Fetch API
|
|
261
|
+
as a first class citizen. So the configuration is really simple like any other JS runtime;
|
|
202
262
|
|
|
203
263
|
```ts
|
|
204
264
|
import myServerAdapter from './myServerAdapter'
|
|
@@ -212,9 +272,11 @@ console.info(`Server is running on ${server.hostname}`)
|
|
|
212
272
|
|
|
213
273
|
## File Uploads / Multipart Requests
|
|
214
274
|
|
|
215
|
-
Multipart requests are a type of HTTP request that allows you to send blobs together with regular
|
|
275
|
+
Multipart requests are a type of HTTP request that allows you to send blobs together with regular
|
|
276
|
+
text data which has a mime-type `multipart/form-data`.
|
|
216
277
|
|
|
217
|
-
For example, if you send a multipart request from a browser with `FormData`, you can get the same
|
|
278
|
+
For example, if you send a multipart request from a browser with `FormData`, you can get the same
|
|
279
|
+
`FormData` object in your request handler.
|
|
218
280
|
|
|
219
281
|
```ts
|
|
220
282
|
import { createServerAdapter } from '@whatwg-node/server'
|
|
@@ -238,15 +300,21 @@ const myServerAdapter = createServerAdapter(async request => {
|
|
|
238
300
|
})
|
|
239
301
|
```
|
|
240
302
|
|
|
241
|
-
You can learn more about [File API](https://developer.mozilla.org/en-US/docs/Web/API/File) on MDN
|
|
303
|
+
You can learn more about [File API](https://developer.mozilla.org/en-US/docs/Web/API/File) on MDN
|
|
304
|
+
documentation.
|
|
242
305
|
|
|
243
306
|
## Routing and Middlewares
|
|
244
307
|
|
|
245
|
-
We'd recommend to use `@whatwg-node/router` to handle routing and middleware approach. It uses
|
|
308
|
+
We'd recommend to use `@whatwg-node/router` to handle routing and middleware approach. It uses
|
|
309
|
+
`@whatwg-node/server` under the hood.
|
|
310
|
+
|
|
311
|
+
> Learn more about `@whatwg-node/router` [here](../router)
|
|
246
312
|
|
|
247
313
|
### Basic Routing
|
|
248
314
|
|
|
249
315
|
```ts
|
|
316
|
+
// Then use it in any environment
|
|
317
|
+
import { createServer } from 'http'
|
|
250
318
|
import { createRouter, Router } from '@whatwg-node/router'
|
|
251
319
|
|
|
252
320
|
const router = createRouter()
|
|
@@ -266,15 +334,14 @@ router.get('/google', () => Response.redirect('http://www.google.com'))
|
|
|
266
334
|
// 404 for everything else
|
|
267
335
|
router.all('*', () => new Response('Not Found.', { status: 404 }))
|
|
268
336
|
|
|
269
|
-
// Then use it in any environment
|
|
270
|
-
import { createServer } from 'http'
|
|
271
337
|
const httpServer = createServer(router)
|
|
272
338
|
httpServer.listen(4000)
|
|
273
339
|
```
|
|
274
340
|
|
|
275
341
|
### Middlewares to handle CORS, cookies and more
|
|
276
342
|
|
|
277
|
-
This package also provides some utilities for your platform agnostic server implementation. The
|
|
343
|
+
This package also provides some utilities for your platform agnostic server implementation. The
|
|
344
|
+
following example shows how to get the cookies as an object from the request.
|
|
278
345
|
|
|
279
346
|
```ts
|
|
280
347
|
import { withCookies } from '@whatwg-node/server'
|
package/index.js
CHANGED
|
@@ -5,7 +5,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
const fetch = require('@whatwg-node/fetch');
|
|
6
6
|
|
|
7
7
|
function isAsyncIterable(body) {
|
|
8
|
-
return body != null && typeof body === 'object' && typeof body[Symbol.asyncIterator] === 'function';
|
|
8
|
+
return (body != null && typeof body === 'object' && typeof body[Symbol.asyncIterator] === 'function');
|
|
9
9
|
}
|
|
10
10
|
function getPort(nodeRequest) {
|
|
11
11
|
var _a, _b, _c, _d, _e;
|
|
@@ -128,7 +128,11 @@ function isNodeRequest(request) {
|
|
|
128
128
|
}
|
|
129
129
|
function isServerResponse(stream) {
|
|
130
130
|
// Check all used functions are defined
|
|
131
|
-
return (stream != null &&
|
|
131
|
+
return (stream != null &&
|
|
132
|
+
stream.setHeader != null &&
|
|
133
|
+
stream.end != null &&
|
|
134
|
+
stream.once != null &&
|
|
135
|
+
stream.write != null);
|
|
132
136
|
}
|
|
133
137
|
function isReadableStream(stream) {
|
|
134
138
|
return stream != null && stream.getReader != null;
|
|
@@ -203,7 +207,9 @@ function createServerAdapter(serverAdapterBaseObject,
|
|
|
203
207
|
* WHATWG Fetch spec compliant `Request` constructor.
|
|
204
208
|
*/
|
|
205
209
|
RequestCtor = fetch.Request) {
|
|
206
|
-
const handleRequest = typeof serverAdapterBaseObject === 'function'
|
|
210
|
+
const handleRequest = typeof serverAdapterBaseObject === 'function'
|
|
211
|
+
? serverAdapterBaseObject
|
|
212
|
+
: serverAdapterBaseObject.handle;
|
|
207
213
|
function handleNodeRequest(nodeRequest, ...ctx) {
|
|
208
214
|
const serverContext = ctx.length > 1 ? completeAssign({}, ...ctx) : ctx[0];
|
|
209
215
|
const request = normalizeNodeRequest(nodeRequest, RequestCtor);
|
|
@@ -372,7 +378,9 @@ function getCORSHeadersByRequestAndOptions(request, corsOptions) {
|
|
|
372
378
|
return headers;
|
|
373
379
|
}
|
|
374
380
|
// If defined origins have '*' or undefined by any means, we should allow all origins
|
|
375
|
-
if (corsOptions.origin == null ||
|
|
381
|
+
if (corsOptions.origin == null ||
|
|
382
|
+
corsOptions.origin.length === 0 ||
|
|
383
|
+
corsOptions.origin.includes('*')) {
|
|
376
384
|
const currentOrigin = request.headers.get('origin');
|
|
377
385
|
// If origin is available in the headers, use it
|
|
378
386
|
if (currentOrigin != null) {
|
package/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { Request, Response } from '@whatwg-node/fetch';
|
|
|
2
2
|
export { Response } from '@whatwg-node/fetch';
|
|
3
3
|
|
|
4
4
|
function isAsyncIterable(body) {
|
|
5
|
-
return body != null && typeof body === 'object' && typeof body[Symbol.asyncIterator] === 'function';
|
|
5
|
+
return (body != null && typeof body === 'object' && typeof body[Symbol.asyncIterator] === 'function');
|
|
6
6
|
}
|
|
7
7
|
function getPort(nodeRequest) {
|
|
8
8
|
var _a, _b, _c, _d, _e;
|
|
@@ -125,7 +125,11 @@ function isNodeRequest(request) {
|
|
|
125
125
|
}
|
|
126
126
|
function isServerResponse(stream) {
|
|
127
127
|
// Check all used functions are defined
|
|
128
|
-
return (stream != null &&
|
|
128
|
+
return (stream != null &&
|
|
129
|
+
stream.setHeader != null &&
|
|
130
|
+
stream.end != null &&
|
|
131
|
+
stream.once != null &&
|
|
132
|
+
stream.write != null);
|
|
129
133
|
}
|
|
130
134
|
function isReadableStream(stream) {
|
|
131
135
|
return stream != null && stream.getReader != null;
|
|
@@ -200,7 +204,9 @@ function createServerAdapter(serverAdapterBaseObject,
|
|
|
200
204
|
* WHATWG Fetch spec compliant `Request` constructor.
|
|
201
205
|
*/
|
|
202
206
|
RequestCtor = Request) {
|
|
203
|
-
const handleRequest = typeof serverAdapterBaseObject === 'function'
|
|
207
|
+
const handleRequest = typeof serverAdapterBaseObject === 'function'
|
|
208
|
+
? serverAdapterBaseObject
|
|
209
|
+
: serverAdapterBaseObject.handle;
|
|
204
210
|
function handleNodeRequest(nodeRequest, ...ctx) {
|
|
205
211
|
const serverContext = ctx.length > 1 ? completeAssign({}, ...ctx) : ctx[0];
|
|
206
212
|
const request = normalizeNodeRequest(nodeRequest, RequestCtor);
|
|
@@ -369,7 +375,9 @@ function getCORSHeadersByRequestAndOptions(request, corsOptions) {
|
|
|
369
375
|
return headers;
|
|
370
376
|
}
|
|
371
377
|
// If defined origins have '*' or undefined by any means, we should allow all origins
|
|
372
|
-
if (corsOptions.origin == null ||
|
|
378
|
+
if (corsOptions.origin == null ||
|
|
379
|
+
corsOptions.origin.length === 0 ||
|
|
380
|
+
corsOptions.origin.includes('*')) {
|
|
373
381
|
const currentOrigin = request.headers.get('origin');
|
|
374
382
|
// If origin is available in the headers, use it
|
|
375
383
|
if (currentOrigin != null) {
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@whatwg-node/server",
|
|
3
|
-
"version": "0.5.9
|
|
3
|
+
"version": "0.5.9",
|
|
4
4
|
"description": "Fetch API compliant HTTP Server adapter",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@whatwg-node/fetch": "0.6.3
|
|
7
|
+
"@whatwg-node/fetch": "0.6.3",
|
|
8
8
|
"tslib": "^2.3.1"
|
|
9
9
|
},
|
|
10
10
|
"repository": {
|