@TypedRoute
export namespace TypedRoute {
export function Get(path?: string): MethodDecorator;
export function Post(path?: string): MethodDecorator;
export function Put(path?: string): MethodDecorator;
export function Patch(path?: string): MethodDecorator;
export function Delete(path?: string): MethodDecorator;
}Drop-in replacement for @Get, @Post, @Put, @Patch, @Delete from @nestjs/common. Behavior is identical plus:
- The response value is type-checked at runtime against the return type.
- JSON serialization is up to 200× faster than
class-transformer. - The endpoint becomes visible to
@nestia/sdk— it appears in the generated SDK, Swagger document, and e2e suite.
If a controller method returns something that doesn’t match its declared return type, the server throws 500 Internal Server Error instead of sending malformed JSON to the client. That alone catches a category of bugs you would otherwise find in production.
Basic usage
import { TypedRoute } from "@nestia/core";
import { Controller } from "@nestjs/common";
import { tags } from "typia";
interface IArticle {
id: string & tags.Format<"uuid">;
title: string;
body: string;
created_at: string & tags.Format<"date-time">;
}
@Controller("articles")
export class ArticlesController {
/**
* Pick a random article.
*/
@TypedRoute.Get("random")
public async random(): Promise<IArticle> {
return {
id: "2b5e21d8-0e44-4482-bd3e-4540dee7f3d6",
title: "Hello Nestia",
body: "Just use TypedRoute.Get() like this.",
created_at: new Date().toISOString(),
};
}
}That’s the entire change from vanilla NestJS — @Get("random") becomes @TypedRoute.Get("random"), and the return type is now enforced.
What gets validated
Everything the type expresses:
- Field presence and primitive types.
tags.*constraints (Format,MinLength,Maximum,Pattern, etc.).- Nested objects and arrays.
- Discriminated unions, recursive shapes, nullable / optional fields.
If validation passes, Nestia stringifies the value with a hand-generated encoder for the exact return type — no generic JSON.stringify recursion, no class-transformer reflection.
The build-time transformer reads that return type and emits the runtime validator and stringifier directly.
When to use plain @Get instead
Mixing is fine. Reach for the vanilla decorator when:
- The method returns a
Stream, aBuffer, or an HTTP redirect — Nestia’s stringifier expects JSON-serializable data. - The route is internal (health checks, metrics) and you don’t want it in the SDK or Swagger output.
- The return type involves
classinstances whosetoJSON()behavior you rely on (Nestia honorstoJSONonPrimitive<T>, but the contract is subtler — see thePrimitivetype in the generated SDK).
Vanilla routes are invisible to @nestia/sdk’s generators, so they don’t pollute your SDK / Swagger / e2e output.
EncryptedRoute
export namespace EncryptedRoute {
export function Get(path?: string): MethodDecorator;
export function Post(path?: string): MethodDecorator;
// ...same signatures as TypedRoute
}Same family, but the response body is AES-128/256 CBC encrypted with PKCS #5 padding and Base64 encoded. Slower than TypedRoute; use when you need transport-level confidentiality the SDK will decrypt automatically.
Encrypted routes do not appear in Swagger UI because the response shape is encrypted. The generated SDK (@nestia/sdk) handles the decryption on the client side, so consumers see typed, decrypted data.
Configuration
No compilerOptions.plugins entry is needed for the default behavior. Without
an explicit plugin config, @TypedRoute uses "assert" for response
serialization and applies no LLM schema restriction.
Add a plugin entry only when you want to override those defaults:
{
"compilerOptions": {
"strict": true,
"plugins": [
{ "transform": "typia/lib/transform", "enabled": false },
{
"transform": "@nestia/core/native/transform.cjs",
"stringify": "validate.log",
"llm": { "strict": true }
}
]
}
}stringify — which serializer to use
The value picks the typia function TypedRoute calls. Each has slightly different runtime behavior:
| Value | Behavior |
|---|---|
"assert" (default) | Validate; on failure throw; on success stringify. |
"stringify" | Skip validation; stringify directly. Fastest, least safe. |
"is" | Type-check; on failure throw 500; on success stringify. |
"validate" | Collect every type error before throwing. |
"validate.log" | Same as "validate", but on error log instead of throwing — useful during a migration. |
null | Use plain JSON.stringify. No validation, no specialized encoder. |
Configure the logger for "validate.log":
import { TypedRoute } from "@nestia/core";
TypedRoute.setValidateErrorLogger((err) => {
console.error(err);
// or ship it to your APM
});llm — restrict to LLM-friendly schemas
"llm": true // forbid tuples
"llm": { "strict": true } // also forbid additionalProperties and optional fieldsWhen set, the transformer rejects any TypedRoute whose return type falls outside the schema subset accepted by LLM function calling. Useful when you intend to expose endpoints to @nestia/chat or another agentic AI runtime — the check happens at build time, not at runtime.
See Swagger → Chat for the full LLM integration story.
Benchmarks
typia.json.stringify<T> is up to 200× faster than class-transformer. Treat that as the peak case; 30–100× is the typical real-server speedup.
@TypedRoute matters in production because JSON stringification is the one HTTP-pipeline step that runs synchronously on the main thread. Slow encoders block subsequent requests. The faster encoder is the kind of free win you would otherwise pay for by migrating to fastify.
See also
- TypedBody — type-safe
application/jsonrequest bodies. - TypedException — typed error responses.