Frameworks

Generate an SDK from NestJS with @nestjs/swagger

NestJS doesn't emit OpenAPI out of the box the way FastAPI does — you add the official @nestjs/swagger module, wire up DocumentBuilder and SwaggerModule in a few lines of main.ts, and you get a full spec plus a Swagger UI. From there, generating a TypeScript or Python SDK is the same problem as any other framework: get a clean spec out, then run it through a generator or a hosted pipeline like Sourced.

Setting up @nestjs/swagger

Install the module:

npm install --save @nestjs/swagger

If your app runs on Fastify instead of Express, also install @fastify/static.

Wire it up in main.ts:

import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module.js';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const config = new DocumentBuilder()
    .setTitle('Cats example')
    .setDescription('The cats API description')
    .setVersion('1.0')
    .addTag('cats')
    .build();
  const documentFactory = () => SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api', app, documentFactory);

  await app.listen(process.env.PORT ?? 3000);
}
await bootstrap();

DocumentBuilder sets the spec's metadata (title, description, version, tags); SwaggerModule.setup('api', app, documentFactory) mounts both the interactive UI and the underlying document at the api path.

Where the raw OpenAPI JSON lives

With the setup above (UI mounted at /api), the raw spec is available at /api-json — the NestJS docs describe navigating there directly to "generate and download a Swagger JSON file." That's the file you feed to a generator or a hosted pipeline.

If you want a different URL for the JSON, SwaggerModule.setup takes an options object:

SwaggerModule.setup('swagger', app, documentFactory, {
  jsonDocumentUrl: 'swagger/json',
});

That mounts the UI at /swagger and the raw spec at /swagger/json.

The gotcha: interfaces don't produce usable schemas

@nestjs/swagger builds its schema information by reading TypeScript metadata at runtime via reflection — and TypeScript interfaces and generics don't exist at runtime; they're erased during compilation. NestJS's own docs state this directly: "Since TypeScript does not store metadata about generics or interfaces, when you use them in your DTOs, SwaggerModule may not be able to properly generate model definitions at runtime."

In practice, that means a DTO written as an interface either produces an empty or incomplete schema, or one with no meaningful name — the same downstream problem covered in fixing inline schema names like Type1 and InlineObject: a schema with nothing for the generator to name it after. Two fixes, both documented by NestJS:

  • Use classes, not interfaces, for every DTO. Classes survive compilation, so reflection can read their shape.
  • Add the Swagger CLI plugin so you don't have to hand-annotate every property with @ApiProperty() — NestJS's docs recommend it explicitly: "consider using the Swagger plugin... which will automatically provide this for you."

If you've already got interface-typed DTOs across a large NestJS app, converting them to classes is mechanical but real work — budget time for it before your first SDK generation pass, not after a customer reports a broken type.

From a NestJS spec to hosted docs and typed SDKs

Once /api-json reflects real DTO classes, turning it into an installable SDK is a separate step. With Sourced:

  1. Point Sourced at your repo or spec URL. Connect from GitHub, or paste the /api-json output directly into a new project.
  2. Preview the TypeScript and Python SDKs. Sourced renders both before publishing — method names, DTO-derived types, typed error classes — so an interface-derived empty schema is visible in the preview, not in a customer's autocomplete.
  3. Get hosted docs and an llms.txt from the same spec. No separate docs deploy — the hosted docs preview and llms.txt come out of the same pass as the SDKs.
  4. Publish on your schedule. Free unlimited previews and up to 2 hosted noindex docs review sites and one hosted MCP server, no credit card, get you through steps 1-3.

Comparison: @nestjs/swagger vs. a full SDK pipeline

Need @nestjs/swagger gives you What it doesn't give you
A generated OpenAPI spec Yes, via DocumentBuilder + SwaggerModule Nothing — this part is real and free
Interactive API browsing Swagger UI at your configured path A hosted docs site for customers
Correct schemas from DTOs Yes, if DTOs are classes Nothing if DTOs are interfaces or generics
A typed client SDK Nothing built in Generated TypeScript or Python SDK
A diff before a breaking release Nothing built in A compatibility report
Agent-readable docs Nothing built in llms.txt derived from the spec

OSS generators for other languages

Sourced's generated SDKs focus on TypeScript and Python today — the two ecosystems where teams publish first, and a natural fit for a TypeScript-first framework like NestJS. For everything else, NestJS's spec works with the standard OpenAPI Generator ecosystem — Go, Java, Ruby, PHP, C#, Kotlin, Rust, and Swift all have their own walkthroughs. The five-minute generate-an-SDK guide covers running a local generator against any spec, NestJS's included.

Honest scope: when @nestjs/swagger alone is enough

If your NestJS API only has other NestJS or TypeScript-native consumers on the same team, a shared @nestjs/axios-based client or a thin fetch wrapper hand-written against your DTOs is a reasonable choice — you already own the types, and a codegen pipeline adds a maintenance surface you might not need yet. Reach for real SDK generation once you have external customers, a second language to support, or a docs site that needs to exist independently of your Swagger UI mount.

FAQ

Does NestJS generate OpenAPI automatically?

Not without the @nestjs/swagger module. Install it, wire up DocumentBuilder and SwaggerModule in main.ts, and the spec is generated from your controllers and DTOs at runtime — but it's an explicit setup step, unlike FastAPI's fully automatic /openapi.json.

Where is the raw OpenAPI JSON in a NestJS app?

At <swagger-ui-path>-json by default — /api-json if your UI is mounted at /api, per NestJS's own docs. You can change this with the jsonDocumentUrl option passed to SwaggerModule.setup.

Why are my DTO fields missing from the generated schema?

Almost always because the DTO is a TypeScript interface or uses generics. @nestjs/swagger reads metadata via reflection, and interfaces and generics are erased at compile time, so there's nothing to reflect on. Convert the DTO to a class.

Do I need to manually add @ApiProperty() to every field?

Not if you install the Swagger CLI plugin, which NestJS's docs recommend specifically to avoid hand-annotating every property. Without it, undecorated class properties may still be missing from the generated schema depending on your TypeScript config.

Does @nestjs/swagger support OpenAPI 3.1?

Check your installed version's changelog before assuming — the OpenAPI version a given @nestjs/swagger release targets has changed across major versions of the package. If your downstream generator or docs tool needs a specific version, validate the emitted document rather than assuming. See OpenAPI 3.1 vs 3.0 for what actually changes between versions.

Can I generate the SDK without running the NestJS server?

You need the app to boot at least far enough for SwaggerModule.createDocument(app, config) to run, since it inspects live controller and provider metadata. A small standalone bootstrap script (no app.listen() call) that calls createDocument and writes the result to a file works without a long-running server.

Ship it

Once @nestjs/swagger is wired up and your DTOs are classes, you have a real OpenAPI spec to work with. Start free on Sourced to preview a TypeScript and Python SDK plus hosted docs from that spec in one pass, or run it through the OpenAPI validator first to catch interface-derived gaps before you generate anything.