Skip to Content
📖 Guide DocumentsSwagger DocumentSwagger Builder

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 a swagger.json file at build time.
  • NestiaSwaggerComposer.document(app) — compose the document inside a running app, serve it through SwaggerModule.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:

src/main.ts
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:

nestia.config.ts
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:

Terminal
npx nestia swagger # or, all three artifacts at once: npx nestia all

Output: 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:

Terminal
npx nestia swagger --watch

Watch 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.1" (default), "3.0", or "2.0". Nestia generates the spec at 3.1 internally and downgrades on the way out — your type fidelity is highest at 3.1, but the document is still legal at older versions.


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 / 201 are inferred from the HTTP method.
  • Error responses from @TypedException() decorators.
  • summary / description from the controller method’s JSDoc.
  • Field descriptions, examples, titles from JSDoc on DTO properties.
  • Readonly array hints through x-readonly-array for TypeScript readonly T[] and ReadonlyArray<T> positions. Property-level readonly prop still maps to OpenAPI readOnly.
  • Constraints (minLength, format, pattern, …) from tags.* intersections.

If a route uses vanilla @Get / @Body, it is invisible to the generator — only @Typed* routes appear in the schema.


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:

FieldTypeNotes
outputstringFile path. If a directory, writes swagger.json inside.
openapi"3.1" | "3.0" | "2.0"Defaults to "3.1".
infoPartial<OpenApi.IDocument.IInfo>title, version, description. Falls back to package.json.
serversOpenApi.IServer[]One entry per environment.
securityRecord<string, OpenApi.ISecurityScheme>Bearer, OAuth, API key, etc.
tagsOpenApi.IDocument.ITag[]Tag descriptions (also overridable per-route via JSDoc @tag).
decomposebooleanSplit query-DTO into one parameter per leaf field.
operationId(props) => stringCustom operationId generator.
beautifybooleanPretty-print the JSON output.

See also

Last updated on