Outline
npx nestia swagger
npx nestia swagger --config nestia.config.ts --project tsconfig.json
Configure nestia.config.ts
file and run npx nestia swagger
command.
Then, @nestia/sdk
will analyze your NestJS backend server code, and generate swagger.json
file.
If you have a special configuration file that its file name is not nestia.config.ts
, you can specify it with --config
option like npx nestia swagger --config another.config.ts
. Also, if you have a special tsconfig.json
file, you can specify it with --project
option like npx nestia swagger --project another.tsconfig.json
, too.
Configuration
import { INestiaConfig } from "@nestia/sdk";
import { NestFactory } from "@nestjs/core";
import { YourModule } from "./src/YourModule";
const NESTIA_CONFIG: INestiaConfig = {
input: async () => {
const app = await NestFactory.create(YourModule);
// app.setGlobalPrefix("api");
// app.enableVersioning({
// type: VersioningType.URI,
// prefix: "v",
// })
return app;
},
swagger: {
output: "dist/swagger.json",
beautify: true,
security: {
bearer: {
type: "apiKey",
name: "Authorization",
in: "header",
},
},
servers: [
{
url: "http://localhost:3000",
description: "Local Server"
}
],
}
};
export default NESTIA_CONFIG;
Make nestia.config.ts
file and run npx nestia swagger
command.
At first, create nestia.config.ts
file through npx nestia init
command. It would be placed on the top level directory of your NestJS backend project. For reference, tsconfig.json
file also must be placed in the top level directory, too. After creation, configure the nestia.config.ts
file referencing above example code and type definition.
At least, you've to configure those two properties:
input
: Accessor of controller classesswagger.output
: Path ofswagger.json
file
When you've completed above configuration, just run npx nestia swagger
command. Then, swagger.json
file would be newly generated, and placed into the $config.swagger.output
directory following your nestia.config.ts
configuration.
By the way, nestia.config.ts
supports alternative options specifying the target controller classes instead of using the Module
instance. If your backend application server does not have special configuration like setGlobalPrefix
, enableVersioning
and RouterModule
, it is okay to specifying the target controller classes just by writing their file path like below.
import { INestiaConfig } from "@nestia/sdk";
const NESTIA_CONFIG: INestiaConfig = {
input: [
"src/controllers",
"src/fake/controllers",
"src/test/controllers",
],
swagger: {
output: "dist/swagger.json",
beautify: true,
security: {
bearer: {
type: "apiKey",
name: "Authorization",
in: "header",
},
},
servers: [
{
url: "http://localhost:3000",
description: "Local Server"
}
],
}
};
export default NESTIA_CONFIG;
Special Tags
Controller Methods
Swagger generator @nestia/sdk
supports three type of comment tags for controller methods:
- Hiding
@deprecated
: mark asdeprecated
@internal
: hide, never be shown@ignore
: hide, never be shown, even in the SDK side
- Labeling
@summary
: short description of endpoint@tag {name}
: grouppig@operationId {value}
: manual operation ID
- Security
@security {key}
: security scheme key@security {key} {...scopes}
: +scopes for OAuth2 type
At first, @internal
and @ignore
tags are used to hide the controller method from the Swagger Documents. When you use one of them, the controller method would not be written in the swagger.json
file. Otherwise, the @deprecated
tag is used to mark the controller method as deprecated. When you use it, Swagger Editor will show the deprecated message about the route method like below.
Also, the @summary
tag is used to write short description of the endpoint. By the way, the @summary
tag can be replaced by writing top sentence ends with .
symbol. The other one, @tag {name}
tag is used for only groupping.
The last one, @security
is a tag for security scheme. It specifies target security scheme by writing its key like @security {key}
. If target scheme type is OAuth2, and it has configured scopes, you can specify the scopes by writing scopes at the backward like @security {key} read write
.
For reference, target security schemes must be configured in the nestia.config.ts
file. If you use @security
tag that is not configured in the nestia.config.ts
file, it would be an error. Also, if you've configured @nestia/swagger
security decorator like @ApiSecurity
, @nestia/sdk
also can recognize it too.
import { TypedBody, TypedParam, TypedRoute } from "@nestia/core";
import { Controller } from "@nestjs/common";
import { ApiSecurity } from "@nestjs/swagger";
import typia, { tags } from "typia";
import { IBbsArticle } from "@api/lib/structures/IBbsArticle";
@Controller("bbs/articles/:section")
export class BbsArticlesController {
/**
* Would be shown without any mark.
*
* @param section Section code
* @param input Content to store
* @returns Newly archived article
*
* @tag public
* @summary Public API
* @security bearer
* @security oauth2 read write
*/
@TypedRoute.Post()
public async store(
@TypedParam("section") section: string,
@TypedBody() input: IBbsArticle.IStore,
): Promise<IBbsArticle> {
return {
...typia.random<IBbsArticle>(),
...input,
section,
};
}
/**
* Deprecated API.
*
* Would be marked as "deprecated".
*
* For reference, top sentence "Deprecated API." can replace the `@summary` tag.
*
* @param section Section code
* @param id Target article ID
* @param input Content to update
* @returns Updated content
*
* @deprecated
* @operationId updateArticle
* @security basic
* @security bearer
*/
@TypedRoute.Put(":id")
public async update(
@TypedParam("section") section: string,
@TypedParam("id") id: string & tags.Format<"uuid">,
@TypedBody() input: IBbsArticle.IStore,
): Promise<IBbsArticle> {
return {
...typia.random<IBbsArticle>(),
...input,
id,
section,
};
}
/**
* Would not be shown.
*
* @internal
*/
@ApiSecurity("custom") // LEGACY DECRATOR ALSO CAN BE USED
@TypedRoute.Delete(":id")
public erase(
@TypedParam("section") section: string,
@TypedParam("id") id: string & tags.Format<"uuid">,
): void {
section;
id;
}
}
DTO Properties
https://swagger.io/docs/specification/data-models/data-types/ (opens in a new tab)
You can utilize comments and tags to construct special fields of JSON schema.
If you write any comment on a property, it would fill the IJsonSchema.description
value. When you utilize Special tags of typia
(opens in a new tab), they would be placed into the proper properties of IJsonSchema
. Below is the list of supported type and comment tags in the @nestia/sdk
.
Also, such type and comment tags of DTO properties can be used to enhance validation logic of @nestia/core
library. Especially, @TypedBody.${method}()
, @TypedParam()
, @TypedRoute()
and @TypedQuery()
functions can use those tags for additional validation.
Let's see how those type and comment tags work with example code.
- number
number & Type<{keyword}>
int32
uint32
uint64
int64
float
double
number & Minimum<{number}>
number & Maximum<{number}>
number & ExclusiveMaximum<{number}>
number & ExclusiveMinimum<{number}>
number & MultipleOf<{number}>
- bigint
bigint & Type<{keyword}>
int64
uint64
bigint & Minimum<{bigint}>
bigint & Maximum<{bigint}>
bigint & ExclusiveMaximum<{bigint}>
bigint & ExclusiveMinimum<{bigint}>
bigint & MultipleOf<{bigint}>
- string
string & MinLength<{number}>
string & MaxLength<{number}>
string & Pattern<{regex}>
string & Format<{keyword}>
email
uuid
ipv4
ipv6
url
date
: YYYY-MM-DDdate-time
:Date.toISOString()
export interface SpecialTag {
/**
* Deprecated tags are just used for marking.
*
* @title Unsigned integer
* @deprecated
*/
type: number & tags.Type<"uint32">;
/**
* Internal tagged property never be shown in JSON schema.
*
* It even doesn't be shown in other `typia` functions like `assert<T>()`.
*
* @internal
*/
internal: number[];
/**
* Hidden tagged property never be shown in JSON schema.
*
* However, it would be shown in other `typia` functions like `stringify<T>()`.
*
* @hidden
*/
hidden: boolean;
/**
* You can limit the range of number.
*
* Also, you can configure `default` property by comment tag.
*
* @default 30
*/
number?: number & tags.ExclusiveMinimum<19> & tags.Maximum<100>;
/**
* You can limit the length of string.
*/
string: string & tags.MinLength<3>;
/**
* You can limit the pattern of string.
*/
pattern: string & tags.Pattern<"^[a-z]+$">;
/**
* You can limit the format of string.
*/
format: null | (string & tags.Format<"date-time">);
/**
* You also can perform union type in type tags.
*/
ip: string & (tags.Format<"ipv4"> | tags.Format<"ipv6">);
/**
* In the Array case, only type tags can limit elements' type.
*/
array: Array<string & tags.Format<"uuid">>
& tags.MinItems<3>
& tags.MaxItems<100>;
}
Distribution
You can choose two options for swagger.json
file distribution.
The 1st is publishing the swagger.json
file in a public repo, and showing it through Swagger Editor like below:
samchon/bbs-backend
: Swagger Editor (opens in a new tab)samchon/fake-iamport-server
: Swagger Editor (opens in a new tab)samchon/fake-toss-payments-server
: Swagger Editor (opens in a new tab)
The 2nd way is to hosting the swagger.json
file in the NestJS backend server.
Read below example code, and follow it on yours:
import fs from "fs";
import { NestFactory } from '@nestjs/core';
import { SwaggerModule } from '@nestjs/swagger';
async function open(): Promise<void> {
const app = await NestFactory.create(...);
const docs = require("...write swagger.json path");
docs.servers = [
{ url: "write your server URL" }
];
SwaggerModule.setup("swagger", app, docs);
await app.listen(8080);
}