@TypedHeaders
export function TypedHeaders(): ParameterDecorator;Drop-in replacement for @Headers() from @nestjs/common. Behavior is identical plus:
- Each declared header is validated against the declared TypeScript type.
- Header values are coerced —
"42"→42,"true"→true, repeated headers →string[]. - A type mismatch returns
400 Bad Request.
For auth tokens (Authorization header) and other identity carries, the typical NestJS pattern is a custom @User() decorator backed by a guard — not @TypedHeaders. Use this decorator for application-defined headers that carry data your handler needs: tenant codes, language preferences, custom x-… headers.
Basic usage
import { TypedHeaders, TypedRoute } from "@nestia/core";
import { Controller } from "@nestjs/common";
interface IRequestHeaders {
"x-tenant": string;
"x-locale"?: "en" | "ko" | "ja";
"x-version": number;
}
@Controller("articles")
export class ArticlesController {
@TypedRoute.Get()
public async list(
@TypedHeaders() headers: IRequestHeaders,
): Promise<IArticle[]> {
const tenant = headers["x-tenant"];
const locale = headers["x-locale"] ?? "en";
// ...
}
}A request without x-tenant returns 400. A request with x-version: abc returns 400. A request with valid headers calls the handler with the typed object.
Header-name conventions
- Lowercase preferred. HTTP header names are case-insensitive on the wire, but Node normalizes them to lowercase. Declare
"x-tenant", not"X-Tenant". - Uppercase tolerated. If the interface declares
"X-Foo", Nestia matches it case-insensitively against the request — the value lands at the casing you declared. - Repeated headers (e.g. multiple
Acceptlines) becomestring[]on the parameter. Declare the field as an array type.
What gets coerced
| Declared type | Wire example | Coerces to |
|---|---|---|
string | "foo" | "foo" |
number | "42" | 42 |
boolean | "true" / "false" | true / false |
"a" | "b" | "a" | "a" (validated) |
string[] | repeated header | ["v1","v2"] |
number[] | repeated header, or "1, 2, 3" as Node joins repeats | [1, 2, 3] |
Optional (?) | header absent | undefined |
Tags layered on top (MinLength, Pattern, etc.) work the same as on body / query types.
When to use plain @Headers instead
Reach for the vanilla decorator when:
- The header carries opaque data (
Authorization: Bearer …) and you delegate parsing to a guard. - You need every header, not a known subset — read them from
@Req(), which the SDK ignores; the SDK and Swagger cannot describe arbitrary header names.
@nestia/sdk reads the declared type of @TypedHeaders() and vanilla @Headers() parameters alike into the SDK and the Swagger document, and holds both to the same header rules: one object of statically named atomic properties, none nullable, arrays only where a header may repeat, and a field-named @Headers("key") of an atomic type or an array of them. A dynamic key such as Record<string, string> is reported as a diagnostic, because no document can list arbitrary headers and the SDK could not send them. The vanilla decorator only skips coercion and validation.
See also
- TypedBody — JSON request bodies.
- TypedQuery — query-string parameters.
- TypedParam — path parameters.