h3
Version:
413 lines (288 loc) • 10.8 kB
Markdown
# Request
> H3 request utilities.
## Body
### `assertBodySize(event, limit)`
Asserts that request body size is within the specified limit.
If body size exceeds the limit, throws a `413` Request Entity Too Large response error.
**Example:**
```ts
app.get("/", async (event) => {
await assertBodySize(event, 10 * 1024 * 1024); // 10MB
const data = await event.req.formData();
});
```
### `readBody(event, options?)`
Reads request body and tries to parse using JSON.parse or URLSearchParams.
By default the body is parsed as JSON (falling back to URL-encoded parsing when the `Content-Type` is `application/x-www-form-urlencoded`). Other body types, such as `multipart/form-data`, must be opted into explicitly via `options.type` and are never auto-detected from the request headers.
**Example:**
```ts
app.post("/", async (event) => {
const body = await readBody(event);
});
```
**Example:**
```ts
app.post("/upload", async (event) => {
const body = await readBody(event, { type: "formData" });
});
```
### `readValidatedBody(event, validate)`
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.
You can use a simple function to validate the body or use a Standard-Schema compatible library like `zod` to define a schema.
**Example:**
```ts
function validateBody(body: any) {
return typeof body === "object" && body !== null;
}
app.post("/", async (event) => {
const body = await readValidatedBody(event, validateBody);
});
```
**Example:**
```ts
import { z } from "zod";
const objectSchema = z.object({
name: z.string().min(3).max(20),
age: z.number({ coerce: true }).positive().int(),
});
app.post("/", async (event) => {
const body = await readValidatedBody(event, objectSchema);
});
```
**Example:**
```ts
import * as v from "valibot";
app.post("/", async (event) => {
const body = await readValidatedBody(
event,
v.object({
name: v.pipe(v.string(), v.minLength(3), v.maxLength(20)),
age: v.pipe(v.number(), v.integer(), v.minValue(1)),
}),
{
onError: ({ issues }) => ({
statusText: "Custom validation error",
message: v.summarize(issues),
}),
},
);
});
```
## Query (HTTP `QUERY` method)
Utilities for the [HTTP `QUERY` method (RFC 10008)](https://www.rfc-editor.org/rfc/rfc10008): advertise the query formats a resource accepts and validate the request `Content-Type`.
### `appendAcceptQuery(event, mediaTypes)`
Advertise the query formats a resource accepts by setting the `Accept-Query` response header (RFC 10008, HTTP `QUERY` method).
The media types are serialized as a [Structured Fields](https://www.rfc-editor.org/rfc/rfc8941) List: the base media type becomes a token and any `;name=value` parameters are emitted with their values as quoted strings.
**Example:**
```ts
app.query("/search", (event) => {
appendAcceptQuery(event, ["application/sql;charset=UTF-8", "application/jsonpath"]);
// Accept-Query: application/sql;charset="UTF-8", application/jsonpath
return handleSearch(event);
});
```
### `requireContentType(event, acceptedTypes)`
Assert that the request `Content-Type` is present and one of the accepted media types, following the requirements of RFC 10008 for the HTTP `QUERY` method.
Throws:
- `400 Bad Request` if the `Content-Type` header is missing.
- `422 Unprocessable Content` if the `Content-Type` header is malformed.
- `415 Unsupported Media Type` if the media type is not accepted.
Accepted types may use wildcards: `*` / `*/*` match anything and `type/*` matches any subtype of `type`.
**Example:**
```ts
app.query("/search", async (event) => {
requireContentType(event, ["application/sql", "application/jsonpath"]);
const body = await readBody(event, { type: "text" });
// ...
});
```
## Cache
### `handleCacheHeaders(event, opts)`
Check request caching headers (`If-None-Match`, `If-Modified-Since`) and add caching headers (Last-Modified, ETag, Cache-Control).
Note: `public` is added by default, but never alongside a caller-supplied `private`/`no-store` directive, so passing `cacheControls: ["private"]` no longer produces a contradictory `public, private`.
## More Request Utils
### `assertMethod(event, expected, allowHead?)`
Asserts that the incoming request method is of the expected type using `isMethod`.
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.
If `allowHead` is `true`, it will allow `HEAD` requests to pass if the expected method is `GET`.
**Example:**
```ts
app.get("/", (event) => {
assertMethod(event, "GET");
// Handle GET request, otherwise throw 405 error
});
```
### `getQuery(event)`
Get parsed query string object from the request URL.
**Example:**
```ts
app.get("/", (event) => {
const query = getQuery(event); // { key: "value", key2: ["value1", "value2"] }
});
```
### `getRequestHost(event, opts: { xForwardedHost? })`
Get the request hostname.
If `xForwardedHost` is `true`, it will use the `x-forwarded-host` header if it exists.
If no host header is found, it will return an empty string.
**Example:**
```ts
app.get("/", (event) => {
const host = getRequestHost(event); // "example.com"
});
```
### `getRequestIP(event)`
Try to get the client IP address from the incoming request.
If `xForwardedFor` is `true`, it will use the `x-forwarded-for` header if it exists.
If IP cannot be determined, it will default to `undefined`.
**Example:**
```ts
app.get("/", (event) => {
const ip = getRequestIP(event); // "192.0.2.0"
});
```
### `getRequestProtocol(event, opts: { xForwardedProto? })`
Get the request protocol.
If `x-forwarded-proto` header is set to "https", it will return "https". If the header contains a comma-separated list of protocols, the first entry is used. You can disable this behavior by setting `xForwardedProto` to `false`.
If protocol cannot be determined, it will default to "http".
**Example:**
```ts
app.get("/", (event) => {
const protocol = getRequestProtocol(event); // "https"
});
```
### `getRequestURL(event, opts: { xForwardedHost?, xForwardedProto? })`
Generated the full incoming request URL.
If `xForwardedHost` is `true`, it will use the `x-forwarded-host` header if it exists.
If `xForwardedProto` is `false`, it will not use the `x-forwarded-proto` header.
**Example:**
```ts
app.get("/", (event) => {
const url = getRequestURL(event); // "https://example.com/path"
});
```
### `getRouterParam(event, name, opts: { decode? })`
Get a matched route param by name.
If `decode` option is `true`, it will decode the matched route param (like `decodeURIComponent`), except encoded path separators (`%2f`, `%5c`) are kept encoded so decoding can never reintroduce a `/` or `\` the router never matched.
**Example:**
```ts
app.get("/", (event) => {
const param = getRouterParam(event, "key");
});
```
### `getRouterParams(event, opts: { decode? })`
Get matched route params.
If `decode` option is `true`, it will decode the matched route params (like `decodeURIComponent`), except encoded path separators (`%2f`, `%5c`) are kept encoded so decoding can never reintroduce a `/` or `\` the router never matched.
**Example:**
```ts
app.get("/", (event) => {
const params = getRouterParams(event); // { key: "value" }
});
```
### `getValidatedQuery(event, validate)`
Get the query param from the request URL validated with validate function.
You can use a simple function to validate the query object or use a Standard-Schema compatible library like `zod` to define a schema.
**Example:**
```ts
app.get("/", async (event) => {
const query = await getValidatedQuery(event, (data) => {
return "key" in data && typeof data.key === "string";
});
});
```
**Example:**
```ts
import { z } from "zod";
app.get("/", async (event) => {
const query = await getValidatedQuery(
event,
z.object({
key: z.string(),
}),
);
});
```
**Example:**
```ts
import * as v from "valibot";
app.get("/", async (event) => {
const params = await getValidatedQuery(
event,
v.object({
key: v.string(),
}),
{
onError: ({ issues }) => ({
statusText: "Custom validation error",
message: v.summarize(issues),
}),
},
);
});
```
### `getValidatedRouterParams(event, validate)`
Get matched route params and validate with validate function.
If `decode` option is `true`, it will decode the matched route params (like `decodeURIComponent`), except encoded path separators (`%2f`, `%5c`) are kept encoded so decoding can never reintroduce a `/` or `\` the router never matched.
You can use a simple function to validate the params object or use a Standard-Schema compatible library like `zod` to define a schema.
**Example:**
```ts
app.get("/:key", async (event) => {
const params = await getValidatedRouterParams(event, (data) => {
return "key" in data && typeof data.key === "string";
});
});
```
**Example:**
```ts
import { z } from "zod";
app.get("/:key", async (event) => {
const params = await getValidatedRouterParams(
event,
z.object({
key: z.string(),
}),
);
});
```
**Example:**
```ts
import * as v from "valibot";
app.get("/:key", async (event) => {
const params = await getValidatedRouterParams(
event,
v.object({
key: v.pipe(v.string(), v.picklist(["route-1", "route-2", "route-3"])),
}),
{
decode: true,
onError: ({ issues }) => ({
statusText: "Custom validation error",
message: v.summarize(issues),
}),
},
);
});
```
### `isMethod(event, expected, allowHead?)`
Checks if the incoming request method is of the expected type.
If `allowHead` is `true`, it will allow `HEAD` requests to pass if the expected method is `GET`.
**Example:**
```ts
app.get("/", (event) => {
if (isMethod(event, "GET")) {
// Handle GET request
} else if (isMethod(event, ["POST", "PUT"])) {
// Handle POST or PUT request
}
});
```
### `requestWithBaseURL(req, base)`
Create a lightweight request proxy with the base path stripped from the URL pathname.
### `requestWithURL(req, url)`
Create a lightweight request proxy that overrides only the URL.
Avoids cloning the original request (no `new Request()` allocation).
### `toRequest(input, options?)`
Convert input into a web [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request).
If input is a relative URL, it will be normalized into a full path based on headers.
If input is already a Request and no options are provided, it will be returned as-is.
### `getRequestFingerprint(event, opts)`
Get a unique fingerprint for the incoming request.