h3 2.0.1-rc.2 → 2.0.1-rc.21
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/bin/h3.mjs +35 -0
- package/dist/THIRD-PARTY-LICENSES.md +70 -0
- package/dist/_entries/bun.d.mts +3 -8
- package/dist/_entries/bun.mjs +3 -9
- package/dist/_entries/cloudflare.d.mts +4 -9
- package/dist/_entries/cloudflare.mjs +3 -9
- package/dist/_entries/deno.d.mts +3 -8
- package/dist/_entries/deno.mjs +3 -9
- package/dist/_entries/generic.d.mts +3 -8
- package/dist/_entries/generic.mjs +3 -9
- package/dist/_entries/node.d.mts +3 -8
- package/dist/_entries/node.mjs +3 -12
- package/dist/_entries/service-worker.d.mts +3 -8
- package/dist/_entries/service-worker.mjs +3 -9
- package/dist/docs/0.guide/0.index/index.md +117 -0
- package/dist/docs/0.guide/1.basics/0.lifecycle.md +68 -0
- package/dist/docs/0.guide/1.basics/1.routing.md +92 -0
- package/dist/docs/0.guide/1.basics/2.middleware.md +97 -0
- package/dist/docs/0.guide/1.basics/3.handler.md +165 -0
- package/dist/docs/0.guide/1.basics/4.response.md +162 -0
- package/dist/docs/0.guide/1.basics/5.error.md +117 -0
- package/dist/docs/0.guide/1.basics/6.nested-apps.md +57 -0
- package/dist/docs/0.guide/2.api/0.h3.md +145 -0
- package/dist/docs/0.guide/2.api/1.h3event.md +113 -0
- package/dist/docs/0.guide/3.advanced/0.plugins.md +50 -0
- package/dist/docs/0.guide/3.advanced/1.websocket.md +124 -0
- package/dist/docs/0.guide/3.advanced/2.nightly.md +13 -0
- package/dist/docs/1.utils/0.index/index.md +46 -0
- package/dist/docs/1.utils/1.request.md +355 -0
- package/dist/docs/1.utils/2.response.md +144 -0
- package/dist/docs/1.utils/3.cookie.md +33 -0
- package/dist/docs/1.utils/4.security.md +109 -0
- package/dist/docs/1.utils/5.proxy.md +31 -0
- package/dist/docs/1.utils/6.mcp.md +71 -0
- package/dist/docs/1.utils/7.more.md +78 -0
- package/dist/docs/1.utils/8.community.md +42 -0
- package/dist/docs/2.examples/0.index/index.md +16 -0
- package/dist/docs/2.examples/1.handle-cookie.md +67 -0
- package/dist/docs/2.examples/2.handle-session.md +130 -0
- package/dist/docs/2.examples/3.serve-static-assets.md +66 -0
- package/dist/docs/2.examples/4.stream-response.md +76 -0
- package/dist/docs/2.examples/5.validate-data.md +193 -0
- package/dist/docs/3.migration/0.index/index.md +200 -0
- package/dist/docs/README.md +35 -0
- package/dist/{h3.mjs → h3-CRCltuUf.mjs} +915 -1218
- package/dist/h3-D76FUMrE.d.mts +833 -0
- package/dist/h3-DagAgogP.mjs +4 -0
- package/dist/{h3.d.mts → h3-DiSMXP1G.d.mts} +320 -656
- package/dist/tracing.d.mts +24 -0
- package/dist/tracing.mjs +76 -0
- package/package.json +56 -44
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# H3Event
|
|
2
|
+
|
|
3
|
+
> H3Event, carries incoming request, prepared response and context.
|
|
4
|
+
|
|
5
|
+
With each HTTP request, H3 internally creates an `H3Event` object and passes it though event handlers until sending the response.
|
|
6
|
+
|
|
7
|
+
<read-more></read-more>
|
|
8
|
+
|
|
9
|
+
An event is passed through all the lifecycle hooks and composable utils to use it as context.
|
|
10
|
+
|
|
11
|
+
**Example:**
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
app.get("/", async (event) => {
|
|
15
|
+
// Log HTTP request
|
|
16
|
+
console.log(`[${event.req.method}] ${event.req.url}`);
|
|
17
|
+
|
|
18
|
+
// Parsed URL and query params
|
|
19
|
+
const searchParams = event.url.searchParams;
|
|
20
|
+
|
|
21
|
+
// Try to read request JSON body
|
|
22
|
+
const jsonBody = await event.req.json().catch(() => {});
|
|
23
|
+
|
|
24
|
+
return "OK";
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## `H3Event` Methods
|
|
29
|
+
|
|
30
|
+
### `H3Event.waitUntil`
|
|
31
|
+
|
|
32
|
+
Tell the runtime about an ongoing operation that shouldn't close until the promise resolves.
|
|
33
|
+
|
|
34
|
+
```js [app.mjs]
|
|
35
|
+
import { logRequest } from "./tracing.mjs";
|
|
36
|
+
|
|
37
|
+
app.get("/", (event) => {
|
|
38
|
+
request.waitUntil(logRequest(request));
|
|
39
|
+
return "OK";
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```js [tracing.mjs]
|
|
44
|
+
export async function logRequest(request) {
|
|
45
|
+
await fetch("https://telemetry.example.com", {
|
|
46
|
+
method: "POST",
|
|
47
|
+
body: JSON.stringify({
|
|
48
|
+
method: request.method,
|
|
49
|
+
url: request.url,
|
|
50
|
+
ip: request.ip,
|
|
51
|
+
}),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## `H3Event` Properties
|
|
57
|
+
|
|
58
|
+
### `H3Event.app?`
|
|
59
|
+
|
|
60
|
+
Access to the H3 [application instance](/guide/api/h3).
|
|
61
|
+
|
|
62
|
+
### `H3Event.context`
|
|
63
|
+
|
|
64
|
+
The context is an object that contains arbitrary information about the request.
|
|
65
|
+
|
|
66
|
+
You can store your custom properties inside `event.context` to share across utils.
|
|
67
|
+
|
|
68
|
+
**Known context keys:**
|
|
69
|
+
|
|
70
|
+
- `context.params`: Matched router parameters.
|
|
71
|
+
- `middlewareParams`: Matched middleware parameters
|
|
72
|
+
- `matchedRoute`: Matched router route object.
|
|
73
|
+
- `sessions`: Cached session data.
|
|
74
|
+
- `basicAuth`: Basic authentication data.
|
|
75
|
+
|
|
76
|
+
### `H3Event.req`
|
|
77
|
+
Incoming HTTP request info based on native [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) with additional runtime addons (see [srvx docs](https://srvx.h3.dev/guide/handler#extended-request-context)).
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
app.get("/", async (event) => {
|
|
81
|
+
const url = event.req.url;
|
|
82
|
+
const method = event.req.method;
|
|
83
|
+
const headers = event.req.headers;
|
|
84
|
+
|
|
85
|
+
// (note: you can consume body only once with either of this)
|
|
86
|
+
const bodyStream = await event.req.body;
|
|
87
|
+
const textBody = await event.req.text();
|
|
88
|
+
const jsonBody = await event.req.json();
|
|
89
|
+
const formDataBody = await event.req.formData();
|
|
90
|
+
|
|
91
|
+
return "OK";
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### `H3Event.url`
|
|
96
|
+
|
|
97
|
+
Access to the full parsed request [URL](https://developer.mozilla.org/en-US/docs/Web/API/URL).
|
|
98
|
+
|
|
99
|
+
### `H3Event.res`
|
|
100
|
+
|
|
101
|
+
Prepared HTTP response status and headers.
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
app.get("/", (event) => {
|
|
105
|
+
event.res.status = 200;
|
|
106
|
+
event.res.statusText = "OK";
|
|
107
|
+
event.res.headers.set("x-test", "works");
|
|
108
|
+
|
|
109
|
+
return "OK";
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
<read-more></read-more>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Plugins
|
|
2
|
+
|
|
3
|
+
> H3 plugins allow you to extend an H3 app instance with reusable logic.
|
|
4
|
+
|
|
5
|
+
## Register Plugins
|
|
6
|
+
|
|
7
|
+
Plugins can be registered either when creating a new [H3 instance](/guide/api/h3) or by using [H3.register](/guide/api/h3#h3register).
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
import { H3 } from "h3";
|
|
11
|
+
import { logger } from "./logger.mjs";
|
|
12
|
+
|
|
13
|
+
// Using instance config
|
|
14
|
+
const app = new H3({
|
|
15
|
+
plugins: [logger()],
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Or register later
|
|
19
|
+
app.register(logger());
|
|
20
|
+
|
|
21
|
+
// ... rest of the code..
|
|
22
|
+
app.get("/**", () => "Hello, World!");
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
> [!NOTE]
|
|
26
|
+
> Plugins are always registered immediately. Therefore, the order in which they are used might be important depending on the plugin's functionality.
|
|
27
|
+
|
|
28
|
+
## Creating Plugins
|
|
29
|
+
|
|
30
|
+
H3 plugins are simply functions that accept an [H3 instance](/guide/api/h3) as the first argument and immediately apply logic to extend it.
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
app.register((app) => {
|
|
34
|
+
app.use(...)
|
|
35
|
+
})
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
For convenience, H3 provides a built-in `definePlugin` utility, which creates a typed factory function with optional plugin-specific options.
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
import { definePlugin } from "h3";
|
|
42
|
+
|
|
43
|
+
const logger = definePlugin((h3, _options) => {
|
|
44
|
+
if (h3.config.debug) {
|
|
45
|
+
h3.use((req) => {
|
|
46
|
+
console.log(`[${req.method}] ${req.url}`);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
```
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# WebSockets
|
|
2
|
+
|
|
3
|
+
> H3 has built-in utilities for cross platform WebSocket and Server-Sent Events.
|
|
4
|
+
|
|
5
|
+
You can add cross platform WebSocket support to H3 servers using [🔌 CrossWS](https://crossws.h3.dev/).
|
|
6
|
+
|
|
7
|
+
> [!IMPORTANT]
|
|
8
|
+
> Built-in support of WebSockets in h3 version is WIP.
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
WebSocket handlers can be defined using the `defineWebSocketHandler()` utility and registered to any route like event handlers.
|
|
13
|
+
|
|
14
|
+
You need to register CrossWS as a server plugin in the `serve` function and provide a `resolve` function to resolve the correct hooks from the route.
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
import { H3, serve, defineWebSocketHandler } from "h3";
|
|
18
|
+
|
|
19
|
+
import { plugin as ws } from "crossws/server";
|
|
20
|
+
|
|
21
|
+
const app = new H3();
|
|
22
|
+
|
|
23
|
+
app.get("/_ws", defineWebSocketHandler({ message: console.log }));
|
|
24
|
+
|
|
25
|
+
serve(app, {
|
|
26
|
+
plugins: [ws({ resolve: async (req) => (await app.fetch(req)).crossws })],
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Full example:**
|
|
31
|
+
|
|
32
|
+
```js [websocket.mjs]
|
|
33
|
+
import { H3, serve, defineWebSocketHandler } from "h3";
|
|
34
|
+
import { plugin as ws } from "crossws/server";
|
|
35
|
+
|
|
36
|
+
export const app = new H3();
|
|
37
|
+
|
|
38
|
+
const demoURL =
|
|
39
|
+
"https://raw.githubusercontent.com/h3js/crossws/refs/heads/main/playground/public/index.html";
|
|
40
|
+
|
|
41
|
+
app.get("/", () =>
|
|
42
|
+
fetch(demoURL).then(
|
|
43
|
+
(res) => new Response(res.body, { headers: { "Content-Type": "text/html" } }),
|
|
44
|
+
),
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
app.get(
|
|
48
|
+
"/_ws",
|
|
49
|
+
defineWebSocketHandler({
|
|
50
|
+
// upgrade(req) {},
|
|
51
|
+
open(peer) {
|
|
52
|
+
console.log("[open]", peer);
|
|
53
|
+
|
|
54
|
+
// Send welcome to the new client
|
|
55
|
+
peer.send("Welcome to the server!");
|
|
56
|
+
|
|
57
|
+
// Join new client to the "chat" channel
|
|
58
|
+
peer.subscribe("chat");
|
|
59
|
+
|
|
60
|
+
// Notify every other connected client
|
|
61
|
+
peer.publish("chat", `[system] ${peer} joined!`);
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
message(peer, message) {
|
|
65
|
+
console.log("[message]", peer);
|
|
66
|
+
|
|
67
|
+
if (message.text() === "ping") {
|
|
68
|
+
// Reply to the client with a ping response
|
|
69
|
+
peer.send("pong");
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// The server re-broadcasts incoming messages to everyone
|
|
74
|
+
peer.publish("chat", `[${peer}] ${message}`);
|
|
75
|
+
|
|
76
|
+
// Echo the message back to the sender
|
|
77
|
+
peer.send(message);
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
close(peer) {
|
|
81
|
+
console.log("[close]", peer);
|
|
82
|
+
peer.publish("chat", `[system] ${peer} has left the chat!`);
|
|
83
|
+
peer.unsubscribe("chat");
|
|
84
|
+
},
|
|
85
|
+
}),
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
serve(app, {
|
|
89
|
+
plugins: [ws({ resolve: async (req) => (await app.fetch(req)).crossws })],
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Server-Sent Events (SSE)
|
|
94
|
+
|
|
95
|
+
As an alternative to WebSockets, you can use [Server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events).
|
|
96
|
+
|
|
97
|
+
H3 has a built-in API to create server-sent events using `createEventStream(event)` utility.
|
|
98
|
+
|
|
99
|
+
### Example
|
|
100
|
+
|
|
101
|
+
```js [server-sent-events.mjs]
|
|
102
|
+
import { H3, serve, createEventStream } from "h3";
|
|
103
|
+
|
|
104
|
+
export const app = new H3();
|
|
105
|
+
|
|
106
|
+
app.get("/", (event) => {
|
|
107
|
+
const eventStream = createEventStream(event);
|
|
108
|
+
|
|
109
|
+
// Send a message every second
|
|
110
|
+
const interval = setInterval(async () => {
|
|
111
|
+
await eventStream.push("Hello world");
|
|
112
|
+
}, 1000);
|
|
113
|
+
|
|
114
|
+
// cleanup the interval when the connection is terminated or the writer is closed
|
|
115
|
+
eventStream.onClosed(() => {
|
|
116
|
+
console.log("Connection closed");
|
|
117
|
+
clearInterval(interval);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return eventStream.send();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
serve(app);
|
|
124
|
+
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Nightly Builds
|
|
2
|
+
|
|
3
|
+
You can opt-in to early test latest H3 changes using automated nightly release channel.
|
|
4
|
+
|
|
5
|
+
If you are directly using `h3` as a dependency in your project:
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"h3": "npm:h3-nightly@latest"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# H3 Utils
|
|
2
|
+
|
|
3
|
+
H3 is a composable framework. Instead of providing a big core, you start with a lightweight [H3](/guide/api/h3) instance and for every functionality, there is either a built-in utility or you can make yours.
|
|
4
|
+
|
|
5
|
+
<card-group>
|
|
6
|
+
|
|
7
|
+
<card>
|
|
8
|
+
|
|
9
|
+
Utilities for incoming request.
|
|
10
|
+
</card>
|
|
11
|
+
|
|
12
|
+
<card>
|
|
13
|
+
|
|
14
|
+
Utilities for preparing and sending response.
|
|
15
|
+
</card>
|
|
16
|
+
|
|
17
|
+
<card>
|
|
18
|
+
|
|
19
|
+
Cookie utilities.
|
|
20
|
+
</card>
|
|
21
|
+
|
|
22
|
+
<card>
|
|
23
|
+
|
|
24
|
+
Security utilities.
|
|
25
|
+
</card>
|
|
26
|
+
|
|
27
|
+
<card>
|
|
28
|
+
|
|
29
|
+
Proxy utilities.
|
|
30
|
+
</card>
|
|
31
|
+
|
|
32
|
+
<card>
|
|
33
|
+
|
|
34
|
+
MCP related utilities.
|
|
35
|
+
</card>
|
|
36
|
+
|
|
37
|
+
<card>
|
|
38
|
+
|
|
39
|
+
More Utilities.
|
|
40
|
+
</card>
|
|
41
|
+
|
|
42
|
+
<card>
|
|
43
|
+
|
|
44
|
+
Community made utilities.
|
|
45
|
+
</card>
|
|
46
|
+
</card-group>
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
# Request
|
|
2
|
+
|
|
3
|
+
> H3 request utilities.
|
|
4
|
+
|
|
5
|
+
## Body
|
|
6
|
+
|
|
7
|
+
### `assertBodySize(event, limit)`
|
|
8
|
+
|
|
9
|
+
Asserts that request body size is within the specified limit.
|
|
10
|
+
|
|
11
|
+
If body size exceeds the limit, throws a `413` Request Entity Too Large response error.
|
|
12
|
+
|
|
13
|
+
**Example:**
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
app.get("/", async (event) => {
|
|
17
|
+
await assertBodySize(event, 10 * 1024 * 1024); // 10MB
|
|
18
|
+
const data = await event.req.formData();
|
|
19
|
+
});
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### `readBody(event)`
|
|
23
|
+
|
|
24
|
+
Reads request body and tries to parse using JSON.parse or URLSearchParams.
|
|
25
|
+
|
|
26
|
+
**Example:**
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
app.get("/", async (event) => {
|
|
30
|
+
const body = await readBody(event);
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### `readValidatedBody(event, validate)`
|
|
35
|
+
|
|
36
|
+
Tries to read the request body via `readBody`, then uses the provided validation schema or function and either throws a validation error or returns the result.
|
|
37
|
+
|
|
38
|
+
You can use a simple function to validate the body or use a Standard-Schema compatible library like `zod` to define a schema.
|
|
39
|
+
|
|
40
|
+
**Example:**
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
function validateBody(body: any) {
|
|
44
|
+
return typeof body === "object" && body !== null;
|
|
45
|
+
}
|
|
46
|
+
app.post("/", async (event) => {
|
|
47
|
+
const body = await readValidatedBody(event, validateBody);
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Example:**
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { z } from "zod";
|
|
55
|
+
const objectSchema = z.object({
|
|
56
|
+
name: z.string().min(3).max(20),
|
|
57
|
+
age: z.number({ coerce: true }).positive().int(),
|
|
58
|
+
});
|
|
59
|
+
app.post("/", async (event) => {
|
|
60
|
+
const body = await readValidatedBody(event, objectSchema);
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Example:**
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import * as v from "valibot";
|
|
68
|
+
app.post("/", async (event) => {
|
|
69
|
+
const body = await readValidatedBody(
|
|
70
|
+
event,
|
|
71
|
+
v.object({
|
|
72
|
+
name: v.pipe(v.string(), v.minLength(3), v.maxLength(20)),
|
|
73
|
+
age: v.pipe(v.number(), v.integer(), v.minValue(1)),
|
|
74
|
+
}),
|
|
75
|
+
{
|
|
76
|
+
onError: ({ issues }) => ({
|
|
77
|
+
statusText: "Custom validation error",
|
|
78
|
+
message: v.summarize(issues),
|
|
79
|
+
}),
|
|
80
|
+
},
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Cache
|
|
86
|
+
|
|
87
|
+
### `handleCacheHeaders(event, opts)`
|
|
88
|
+
|
|
89
|
+
Check request caching headers (`If-Modified-Since`) and add caching headers (Last-Modified, Cache-Control) Note: `public` cache control will be added by default
|
|
90
|
+
|
|
91
|
+
## More Request Utils
|
|
92
|
+
|
|
93
|
+
### `assertMethod(event, expected, allowHead?)`
|
|
94
|
+
|
|
95
|
+
Asserts that the incoming request method is of the expected type using `isMethod`.
|
|
96
|
+
|
|
97
|
+
If the method is not allowed, it will throw a 405 error and include an `Allow` response header listing the permitted methods, as required by RFC 9110.
|
|
98
|
+
|
|
99
|
+
If `allowHead` is `true`, it will allow `HEAD` requests to pass if the expected method is `GET`.
|
|
100
|
+
|
|
101
|
+
**Example:**
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
app.get("/", (event) => {
|
|
105
|
+
assertMethod(event, "GET");
|
|
106
|
+
// Handle GET request, otherwise throw 405 error
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### `getQuery(event)`
|
|
111
|
+
|
|
112
|
+
Get parsed query string object from the request URL.
|
|
113
|
+
|
|
114
|
+
**Example:**
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
app.get("/", (event) => {
|
|
118
|
+
const query = getQuery(event); // { key: "value", key2: ["value1", "value2"] }
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### `getRequestHost(event, opts: { xForwardedHost? })`
|
|
123
|
+
|
|
124
|
+
Get the request hostname.
|
|
125
|
+
|
|
126
|
+
If `xForwardedHost` is `true`, it will use the `x-forwarded-host` header if it exists.
|
|
127
|
+
|
|
128
|
+
If no host header is found, it will return an empty string.
|
|
129
|
+
|
|
130
|
+
**Example:**
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
app.get("/", (event) => {
|
|
134
|
+
const host = getRequestHost(event); // "example.com"
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### `getRequestIP(event)`
|
|
139
|
+
|
|
140
|
+
Try to get the client IP address from the incoming request.
|
|
141
|
+
|
|
142
|
+
If `xForwardedFor` is `true`, it will use the `x-forwarded-for` header if it exists.
|
|
143
|
+
|
|
144
|
+
If IP cannot be determined, it will default to `undefined`.
|
|
145
|
+
|
|
146
|
+
**Example:**
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
app.get("/", (event) => {
|
|
150
|
+
const ip = getRequestIP(event); // "192.0.2.0"
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### `getRequestProtocol(event, opts: { xForwardedProto? })`
|
|
155
|
+
|
|
156
|
+
Get the request protocol.
|
|
157
|
+
|
|
158
|
+
If `x-forwarded-proto` header is set to "https", it will return "https". You can disable this behavior by setting `xForwardedProto` to `false`.
|
|
159
|
+
|
|
160
|
+
If protocol cannot be determined, it will default to "http".
|
|
161
|
+
|
|
162
|
+
**Example:**
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
app.get("/", (event) => {
|
|
166
|
+
const protocol = getRequestProtocol(event); // "https"
|
|
167
|
+
});
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### `getRequestURL(event, opts: { xForwardedHost?, xForwardedProto? })`
|
|
171
|
+
|
|
172
|
+
Generated the full incoming request URL.
|
|
173
|
+
|
|
174
|
+
If `xForwardedHost` is `true`, it will use the `x-forwarded-host` header if it exists.
|
|
175
|
+
|
|
176
|
+
If `xForwardedProto` is `false`, it will not use the `x-forwarded-proto` header.
|
|
177
|
+
|
|
178
|
+
**Example:**
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
app.get("/", (event) => {
|
|
182
|
+
const url = getRequestURL(event); // "https://example.com/path"
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### `getRouterParam(event, name, opts: { decode? })`
|
|
187
|
+
|
|
188
|
+
Get a matched route param by name.
|
|
189
|
+
|
|
190
|
+
If `decode` option is `true`, it will decode the matched route param using `decodeURI`.
|
|
191
|
+
|
|
192
|
+
**Example:**
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
app.get("/", (event) => {
|
|
196
|
+
const param = getRouterParam(event, "key");
|
|
197
|
+
});
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### `getRouterParams(event, opts: { decode? })`
|
|
201
|
+
|
|
202
|
+
Get matched route params.
|
|
203
|
+
|
|
204
|
+
If `decode` option is `true`, it will decode the matched route params using `decodeURIComponent`.
|
|
205
|
+
|
|
206
|
+
**Example:**
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
app.get("/", (event) => {
|
|
210
|
+
const params = getRouterParams(event); // { key: "value" }
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### `getValidatedQuery(event, validate)`
|
|
215
|
+
|
|
216
|
+
Get the query param from the request URL validated with validate function.
|
|
217
|
+
|
|
218
|
+
You can use a simple function to validate the query object or use a Standard-Schema compatible library like `zod` to define a schema.
|
|
219
|
+
|
|
220
|
+
**Example:**
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
app.get("/", async (event) => {
|
|
224
|
+
const query = await getValidatedQuery(event, (data) => {
|
|
225
|
+
return "key" in data && typeof data.key === "string";
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**Example:**
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
import { z } from "zod";
|
|
234
|
+
app.get("/", async (event) => {
|
|
235
|
+
const query = await getValidatedQuery(
|
|
236
|
+
event,
|
|
237
|
+
z.object({
|
|
238
|
+
key: z.string(),
|
|
239
|
+
}),
|
|
240
|
+
);
|
|
241
|
+
});
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**Example:**
|
|
245
|
+
|
|
246
|
+
```ts
|
|
247
|
+
import * as v from "valibot";
|
|
248
|
+
app.get("/", async (event) => {
|
|
249
|
+
const params = await getValidatedQuery(
|
|
250
|
+
event,
|
|
251
|
+
v.object({
|
|
252
|
+
key: v.string(),
|
|
253
|
+
}),
|
|
254
|
+
{
|
|
255
|
+
onError: ({ issues }) => ({
|
|
256
|
+
statusText: "Custom validation error",
|
|
257
|
+
message: v.summarize(issues),
|
|
258
|
+
}),
|
|
259
|
+
},
|
|
260
|
+
);
|
|
261
|
+
});
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### `getValidatedRouterParams(event, validate)`
|
|
265
|
+
|
|
266
|
+
Get matched route params and validate with validate function.
|
|
267
|
+
|
|
268
|
+
If `decode` option is `true`, it will decode the matched route params using `decodeURI`.
|
|
269
|
+
|
|
270
|
+
You can use a simple function to validate the params object or use a Standard-Schema compatible library like `zod` to define a schema.
|
|
271
|
+
|
|
272
|
+
**Example:**
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
app.get("/:key", async (event) => {
|
|
276
|
+
const params = await getValidatedRouterParams(event, (data) => {
|
|
277
|
+
return "key" in data && typeof data.key === "string";
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
**Example:**
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
import { z } from "zod";
|
|
286
|
+
app.get("/:key", async (event) => {
|
|
287
|
+
const params = await getValidatedRouterParams(
|
|
288
|
+
event,
|
|
289
|
+
z.object({
|
|
290
|
+
key: z.string(),
|
|
291
|
+
}),
|
|
292
|
+
);
|
|
293
|
+
});
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
**Example:**
|
|
297
|
+
|
|
298
|
+
```ts
|
|
299
|
+
import * as v from "valibot";
|
|
300
|
+
app.get("/:key", async (event) => {
|
|
301
|
+
const params = await getValidatedRouterParams(
|
|
302
|
+
event,
|
|
303
|
+
v.object({
|
|
304
|
+
key: v.pipe(v.string(), v.picklist(["route-1", "route-2", "route-3"])),
|
|
305
|
+
}),
|
|
306
|
+
{
|
|
307
|
+
decode: true,
|
|
308
|
+
onError: ({ issues }) => ({
|
|
309
|
+
statusText: "Custom validation error",
|
|
310
|
+
message: v.summarize(issues),
|
|
311
|
+
}),
|
|
312
|
+
},
|
|
313
|
+
);
|
|
314
|
+
});
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### `isMethod(event, expected, allowHead?)`
|
|
318
|
+
|
|
319
|
+
Checks if the incoming request method is of the expected type.
|
|
320
|
+
|
|
321
|
+
If `allowHead` is `true`, it will allow `HEAD` requests to pass if the expected method is `GET`.
|
|
322
|
+
|
|
323
|
+
**Example:**
|
|
324
|
+
|
|
325
|
+
```ts
|
|
326
|
+
app.get("/", (event) => {
|
|
327
|
+
if (isMethod(event, "GET")) {
|
|
328
|
+
// Handle GET request
|
|
329
|
+
} else if (isMethod(event, ["POST", "PUT"])) {
|
|
330
|
+
// Handle POST or PUT request
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### `requestWithBaseURL(req, base)`
|
|
336
|
+
|
|
337
|
+
Create a lightweight request proxy with the base path stripped from the URL pathname.
|
|
338
|
+
|
|
339
|
+
### `requestWithURL(req, url)`
|
|
340
|
+
|
|
341
|
+
Create a lightweight request proxy that overrides only the URL.
|
|
342
|
+
|
|
343
|
+
Avoids cloning the original request (no `new Request()` allocation).
|
|
344
|
+
|
|
345
|
+
### `toRequest(input, options?)`
|
|
346
|
+
|
|
347
|
+
Convert input into a web [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request).
|
|
348
|
+
|
|
349
|
+
If input is a relative URL, it will be normalized into a full path based on headers.
|
|
350
|
+
|
|
351
|
+
If input is already a Request and no options are provided, it will be returned as-is.
|
|
352
|
+
|
|
353
|
+
### `getRequestFingerprint(event, opts)`
|
|
354
|
+
|
|
355
|
+
Get a unique fingerprint for the incoming request.
|