h3
Version:
148 lines (103 loc) • 5.12 kB
Markdown
# H3Event
> H3Event, carries incoming request, prepared response and context.
With each HTTP request, H3 internally creates an `H3Event` object and passes it though event handlers until sending the response.
<read-more></read-more>
An event is passed through all the lifecycle hooks and composable utils to use it as context.
**Example:**
```js
app.get("/", async (event) => {
// Log HTTP request
console.log(`[${event.req.method}] ${event.req.url}`);
// Parsed URL and query params
const searchParams = event.url.searchParams;
// Try to read request JSON body
const jsonBody = await event.req.json().catch(() => {});
return "OK";
});
```
## `H3Event` Methods
### `H3Event.waitUntil`
Tell the runtime about an ongoing operation that shouldn't close until the promise resolves.
```js [app.mjs]
import { logRequest } from "./tracing.mjs";
app.get("/", (event) => {
request.waitUntil(logRequest(request));
return "OK";
});
```
```js [tracing.mjs]
export async function logRequest(request) {
await fetch("https://telemetry.example.com", {
method: "POST",
body: JSON.stringify({
method: request.method,
url: request.url,
ip: request.ip,
}),
});
}
```
> [!TIP]
> To release per-request resources (timers, upstream connections, file handles) once the event is fully over — on every runtime — use the [`onDispose(event, cb)`](/utils/response#ondisposeevent-cb) utility.
## `H3Event` Properties
### `H3Event.app?`
Access to the H3 [application instance](/guide/api/h3).
### `H3Event.context`
The context is an object that contains arbitrary information about the request.
You can store your custom properties inside `event.context` to share across utils.
**Known context keys:**
- `context.params`: Matched router parameters.
- `middlewareParams`: Matched middleware parameters
- `matchedRoute`: Matched router route object.
- `sessions`: Cached session data.
- `basicAuth`: Basic authentication data.
### `H3Event.req`
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)).
```ts
app.get("/", async (event) => {
const url = event.req.url;
const method = event.req.method;
const headers = event.req.headers;
// (note: you can consume body only once with either of this)
const bodyStream = await event.req.body;
const textBody = await event.req.text();
const jsonBody = await event.req.json();
const formDataBody = await event.req.formData();
return "OK";
});
```
### `H3Event.url`
Access to the full parsed request [URL](https://developer.mozilla.org/en-US/docs/Web/API/URL).
```ts
app.get("/", (event) => {
const { pathname, search, searchParams } = event.url;
return "OK";
});
```
#### Pathname decoding
`event.url.pathname` is percent-decoded **once**, while `event.req.url` keeps the original encoding exactly as it arrived on the wire. H3 decodes eagerly so that route matching and any pathname-based middleware always compare the same normalized value — otherwise a request to `/%61dmin` would slip past an `/admin` guard and still reach the `/admin` route.
Decoding is a single [`decodeURI`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURI) pass, and the result is re-serialized by the URL parser. This means only unreserved escapes visibly decode:
| Request | `event.url.pathname` | Why |
| --- | --- | --- |
| `/%41` | `/A` | Unreserved escape, decodes |
| `/a%2eb` | `/a.b` | Unreserved escape, decodes |
| `/a/%2e%2e/b` | `/b` | Decoded `..` collapses during re-parse |
| `/x%2fy` | `/x%2fy` | Structural, kept encoded |
| `/100%25` | `/100%25` | Structural, kept encoded |
| `/a%20b` | `/a%20b` | Re-encoded by the URL serializer |
| `/caf%C3%A9` | `/caf%C3%A9` | Re-encoded by the URL serializer |
Either way, a route param can never contain a path separator that the router did not match on: `%2f` stays encoded, and `%5c` decodes to `\`, which the URL parser then normalizes into a real `/` the router splits on (`/a%5cb` matches the route `/a/:id`).
> [!WARNING]
> Never decode `event.url.pathname` again. A second `decodeURIComponent` can reintroduce a `/` or `..` that routing and middleware never saw, which is a path traversal vector when the value reaches a filesystem or an upstream URL. To read a route param in decoded form, use [`getRouterParams(event, { decode: true })`](/utils/request#getrouterparamsevent-opts-decode), which decodes everything else but keeps encoded separators encoded.
Requests with malformed percent-encoding (such as `/foo%` or `/%ZZ`) are rejected with a `400 Bad Request` before any handler runs. Set the [`allowMalformedURL`](/guide/api/h3#h3-options) app option to receive the raw pathname instead.
### `H3Event.res`
Prepared HTTP response status and headers.
```ts
app.get("/", (event) => {
event.res.status = 200;
event.res.statusText = "OK";
event.res.headers.set("x-test", "works");
return "OK";
});
```
<read-more></read-more>