Skip to Content

@TypedParam

@nestia/core
export function TypedParam(name: string): ParameterDecorator;

Drop-in replacement for @Param("name") from @nestjs/common. Identical behavior plus:

  • The path parameter is coerced to the TypeScript type ("42" → 42, "42" → 42n for a bigint, "true" → true, "null" → null). A string with a format, such as a date, stays a string and is validated.
  • The coerced value is validated — invalid uuid, out-of-range number, malformed date, etc. become a 400 Bad Request.
  • The parameter is visible to @nestia/sdk — appears in the generated SDK with the same type your handler sees.

Vanilla @Param("id") always gives you a string. @TypedParam("id") gives you exactly the type you declared.

You can reuse an imported scalar alias such as type UuidString = string & tags.Format<"uuid">. Imports resolved through compilerOptions.paths retain the alias’s validation rules during both normal compilation and ttsc --noEmit, including when the paths configuration is inherited.


Basic usage

src/controllers/ArticlesController.ts
import { TypedParam, TypedRoute } from "@nestia/core"; import { Controller } from "@nestjs/common"; import { tags } from "typia"; @Controller("articles") export class ArticlesController { // Path: GET /articles/2b5e21d8-0e44-4482-bd3e-4540dee7f3d6 // `id` is typed as `string & tags.Format<"uuid">` @TypedRoute.Get(":id") public async at( @TypedParam("id") id: string & tags.Format<"uuid">, ): Promise<IArticle> { ... } // Path: GET /articles/section/general/page/42 // `section` is `string`, `page` is `number & tags.Type<"uint32">` @TypedRoute.Get("section/:section/page/:page") public async list( @TypedParam("section") section: string, @TypedParam("page") page: number & tags.Type<"uint32">, ): Promise<IPage<IArticle>> { ... } }

The string "42" arrives at the wire, but page in your handler is already a number. A request to /articles/section/general/page/abc returns 400 before your handler is called.


Supported parameter types

TypeWire exampleCoerces to
string"hello""hello"
string & tags.Format<"uuid">"2b5e2…""2b5e2…" (validated)
string & tags.Format<"date">"2024-04-01""2024-04-01" (validated)
number"42"42
number & tags.Type<"uint32">"42"42 (validated)
bigint"42"42n
boolean"true" / "false"true / false
null"null"null
"a" | "b" | "c""a""a" (validated)
Nullable: T | nullas aboveT or null

Tags layered on top (MinLength, Maximum, Pattern, etc.) work the same as on body / query types.

Path parameters are flat by definition — you cannot decode a nested object out of a single segment. If you need an object, use @TypedQuery or @TypedBody.


When to use plain @Param instead

Reach for the vanilla decorator when:

  • The param is consumed by NestJS-only middleware that expects raw strings.
  • You’re migrating a route piecewise and don’t want to add the dependency yet.

@nestia/sdk reads vanilla params too: the SDK function takes the argument and the Swagger document lists the path parameter, typed as declared. The SDK generators still hold the declared type to the rules @TypedParam() enforces, a single atomic or constant type, and report a diagnostic otherwise; only the handler receives the raw string, without coercion or validation.


Common pitfalls

Validation is on the coerced value, not the raw string. A path of /articles/abc against @TypedParam("id") id: number fails because "abc" cannot become a number — the error is coercion, not a Minimum<…> violation. The 400 response distinguishes between them.

Path-only. @TypedParam reads from path parameters (:name in the route). For querystring values use @TypedQuery; for headers, @TypedHeaders.

A literal colon needs the router’s escape. In a route, :name is a parameter, so a literal :, as in an AIP custom method, is spelled the way your adapter’s router reads it. @nestia/sdk reads both spellings: the SDK sends /items/7:cancel, and the Swagger path is /items/{id}:cancel. @WebSocketRoute accepts both too.

  • Express escapes it as \\:, and escapes *, (, +, and the other characters it reserves the same way.
  • Fastify spells a literal colon ::. This works only in static text: after a parameter, Fastify reads :: as part of the parameter’s name, so it cannot express :id followed by :cancel.
@TypedRoute.Post("items/:id\\:cancel") // Express public cancel(@TypedParam("id") id: string): Promise<void> { ... } @TypedRoute.Post("items::batchGet") // Fastify public batchGet(): Promise<void> { ... }

@nestia/migrate writes the Express spelling for a document path such as /items/{id}:cancel.


See also

  • TypedQuery — query-string parameters, including nested DTOs.
  • TypedBody — JSON request bodies.
  • TypedRoute — the route decorators these parameters live on.
Last updated on