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.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/201are inferred from the HTTP method. - 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.
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:
| Field | Type | Notes |
|---|---|---|
output | string | File path. If a directory, writes swagger.json inside. |
openapi | "3.1" | "3.0" | "2.0" | Defaults to "3.1". |
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 | Split query-DTO into one parameter per leaf field. |
operationId | (props) => string | Custom operationId generator. |
beautify | boolean | Pretty-print the JSON output. |
See also
- Swagger → Strategy — JSDoc tags, security schemes, decomposition.
- Swagger → Editor — embed an in-browser TS playground with Swagger UI.
- Swagger → Chat — convert your OpenAPI document into an LLM agent.