Swagger / OpenAPI
Nestia derives the entire OpenAPI document from your TypeScript types. No @ApiProperty, no @ApiResponse, no separate schema file. Two ways to produce the document:
npx nestia swagger— emit aswagger.jsonfile at build time.NestiaSwaggerComposer.document(app)— compose the document inside a running app, serve it throughSwaggerModule.setup().
Both read the same metadata the Nestia transformer attached at compile time.
Runtime composition
The simplest path. Build the document at app start; serve it through Swagger UI:
import { NestiaSwaggerComposer } from "@nestia/sdk";
import { INestApplication } from "@nestjs/common";
import { NestFactory } from "@nestjs/core";
import { SwaggerModule } from "@nestjs/swagger";
import { AppModule } from "./AppModule";
const main = async () => {
const app: INestApplication = await NestFactory.create(AppModule);
const document = await NestiaSwaggerComposer.document(app, {
openapi: "3.1",
servers: [{ url: "http://localhost:3000", description: "Local" }],
});
SwaggerModule.setup("docs", app, document as any);
await app.listen(3000);
};
main().catch(console.error);Open http://localhost:3000/docs and the Swagger UI loads with your full API. The raw JSON is at http://localhost:3000/docs-json.
Build requirement
NestiaSwaggerComposer needs the @nestia/sdk transform active at build time.
Build with ttsc; it discovers the Nestia and typia transforms from package
manifests. You do not need a compilerOptions.plugins array.
If the app was built with tsc, SWC, Babel, or nest build, NestiaSwaggerComposer
can find zero routes and emit an empty document. The fix is to rebuild through
ttsc.
File generation
For CI pipelines, static hosting, or downstream codegen, emit a file instead:
import { INestiaConfig } from "@nestia/sdk";
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./src/AppModule";
const config: INestiaConfig = {
input: () => NestFactory.create(AppModule),
output: "src/api",
swagger: {
openapi: "3.1", // or "3.0" / "2.0" if your tooling needs it
output: "dist/swagger.json",
info: {
title: "My App",
version: "1.0.0",
},
servers: [
{ url: "http://localhost:3000", description: "Local" },
{ url: "https://api.example.com", description: "Production" },
],
security: {
bearer: { type: "apiKey", name: "Authorization", in: "header" },
},
beautify: true,
},
};
export default config;Run:
npx nestia swagger
# or, all three artifacts at once:
npx nestia allOutput: dist/swagger.json. Feed it to Swagger UI, Redoc, Postman imports, the Nestia Editor, or @nestia/chat.
For local live reload workflows, run the swagger generator in watch mode:
npx nestia swagger --watchWatch mode writes the initial document, then regenerates it when controller, nestia.config.ts, or tsconfig.json inputs change. Point your dev server or Swagger UI reload tooling at the generated file.
OpenAPI versions
The openapi field accepts "3.2" (default), "3.1", "3.0", or "2.0". Nestia composes the document at the newest version and downgrades on the way out — your type fidelity is highest at the newest version, but the document is still legal at older ones.
A Swagger 2.0 document holds less than an OpenAPI 3.x one, so with openapi: "2.0" the generator leaves out or narrows what 2.0 has no place for instead of failing: named examples and request body examples, the x-nestia-encrypted flag (the description keeps the encryption warning), a form body’s own description, and the bodies of exceptions whose media type differs from the success body’s. A form field taking several files, or a file or null, is listed as one file, optional unless the field requires at least one.
What ends up in the schema
The transformer walks each @Typed* decorator and produces:
- Path / query / body / header schemas from the parameter types.
- Response schemas from the return type.
200/201are inferred from the HTTP method. A route whose return type carries no value —void,undefined,never— still declares its status, with no response body attached; the same holds for a@TypedException<void>(). - Error responses from
@TypedException()decorators. summary/descriptionfrom the controller method’s JSDoc.- Field descriptions, examples, titles from JSDoc on DTO properties.
- Readonly array hints through
x-readonly-arrayfor TypeScriptreadonly T[]andReadonlyArray<T>positions. Property-levelreadonly propstill maps to OpenAPIreadOnly. - Constraints (
minLength,format,pattern, …) fromtags.*intersections.
Routes using vanilla @Get / @Body appear too, described from their declared types; they only lose runtime validation. To leave a route out of the document, tag the method @internal or @hidden, or @ignore to leave it out of every generator.
Adding documentation
Use JSDoc on the controller method and DTO fields:
import { tags } from "typia";
/**
* Article payload.
*/
export interface IArticleCreate {
/**
* Article title.
*
* @example "Hello Nestia"
*/
title: string & tags.MinLength<3> & tags.MaxLength<50>;
/**
* Markdown body.
*/
body: string;
}
@Controller("articles")
export class ArticlesController {
/**
* Create article
*
* Stores a new article and returns its assigned ID.
*
* @tag articles
*/
@TypedRoute.Post()
public async create(@TypedBody() input: IArticleCreate): Promise<IArticle> { ... }
}The first JSDoc line becomes the route’s summary, the body becomes the description. Properties get matching schema fields. See Swagger → Strategy for the full list of JSDoc tags Nestia recognizes.
Configuration reference
The swagger block of INestiaConfig:
| Field | Type | Notes |
|---|---|---|
output | string | File path. If a directory, writes swagger.json inside. |
openapi | "3.2" | "3.1" | "3.0" | "2.0" | Defaults to "3.2". |
info | Partial<OpenApi.IDocument.IInfo> | title, version, description. Falls back to package.json. |
servers | OpenApi.IServer[] | One entry per environment. |
security | Record<string, OpenApi.ISecurityScheme> | Bearer, OAuth, API key, etc. |
tags | OpenApi.IDocument.ITag[] | Tag descriptions (also overridable per-route via JSDoc @tag). |
decompose | boolean | Defaults to true: one parameter per property of a query DTO. false keeps it one style: form parameter, except in Swagger 2.0. Headers DTOs are always decomposed. |
operationId | (props) => string | Custom operationId generator. |
beautify | boolean | Pretty-print the JSON output. |
additional | boolean | Adds x-nestia-method, x-nestia-namespace (the SDK accessor), and x-nestia-jsDocTags to each operation. |
Decomposed query and header properties marked with ? produce parameters with required: false, including when exactOptionalPropertyTypes is enabled.
See also
- Swagger → Strategy — JSDoc descriptions, namespaces, function hiding, specialization.
- Swagger → Editor — embed an in-browser TS playground with Swagger UI.
- Swagger → Chat — convert your OpenAPI document into an LLM agent.