Outline
npx nestia sdk
Collection of typed fetch
functions with DTO structures.
Configure nestia.config.ts
file and run npx nestia sdk
command.
Then, @nestia/sdk
will analyze your NestJS backend server code, and generate SDK (Software Development Kit) library composed with. The newly generated SDK library would be composed with DTO
and fetch
functions with type definitions following your NestJS server.
With the SDK library composed with RPC (Remote Procedure Call) functions, you can easily develop e2e test program. Also, frontend developers can utilize the SDK library to interact with your NestJS backend server, much safely and conveniently.
If you can't imagine how the SDK library works, then look at the gif image of below. Left side is the NestJS backend server program, and right side is the Frontend program interacting with your server. Isn't it look like much more convenient and safer than before Swagger Documents case?
Left is NestJS server code, and right is client (frontend) code utilizing SDK
Configuration
Application Module
import { INestiaConfig } from "@nestia/sdk";
import { NestFactory } from "@nestjs/core";
// import { FastifyAdapter } from "@nestjs/platform-fastify";
import { YourModule } from "./src/YourModule";
const NESTIA_CONFIG: INestiaConfig = {
input: async () => {
const app = await NestFactory.create(YourModule);
// const app = await NestFactory.create(YourModule, new FastifyAdapter());
// app.setGlobalPrefix("api");
// app.enableVersioning({
// type: VersioningType.URI,
// prefix: "v",
// })
return app;
},
output: "src/api",
distribute: "packages/api",
};
export default NESTIA_CONFIG;
Make nestia.config.ts
file and run npx nestia sdk
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 classesoutput
: Path of output directory for SDK library
When you've completed above configuration, just run npx nestia sdk
command. Then, SDK library would be newly generated, and placed into the $config.output
directory following your nestia.config.ts
configuration.
Clone Mode
import { INestiaConfig } from "@nestia/sdk";
import { NestFactory } from "@nestjs/core";
// import { FastifyAdapter } from "@nestjs/platform-fastify";
import { YourModule } from "./src/YourModule";
const NESTIA_CONFIG: INestiaConfig = {
input: async () => {
const app = await NestFactory.create(YourModule);
// const app = await NestFactory.create(YourModule, new FastifyAdapter());
// app.setGlobalPrefix("api");
// app.enableVersioning({
// type: VersioningType.URI,
// prefix: "v",
// })
return app;
},
output: "src/api",
clone: true,
distribute: "packages/api",
};
export default NESTIA_CONFIG;
If you configure clone
property to be true
in the nestia.config.ts
file, all of DTO structures used in the backend server would be cloned into the structures
directory, and the SDK library would be refer to the cloned DTO structures instead of the original.
This clone
mode is useful when you'd not separated DTO structures from the ORM models. When you're using TypeORM
or Prisma
, and returning the ORM generated instance directly in the controller without independent DTO structure definition, your SDK library requires the TypeORM
or Prisma
dependency install. By the dependency, client (frontend) developers may install the ORM library that they never need.
In that case, it would better to remove the dependency by using this clone
mode.
Propagation Mode
import { INestiaConfig } from "@nestia/sdk";
import { NestFactory } from "@nestjs/core";
// import { FastifyAdapter } from "@nestjs/platform-fastify";
import { YourModule } from "./src/YourModule";
const config: INestiaConfig = {
input: async () => {
const app = await NestFactory.create(YourModule);
// const app = await NestFactory.create(YourModule, new FastifyAdapter());
// app.setGlobalPrefix("api");
// app.enableVersioning({
// type: VersioningType.URI,
// prefix: "v",
// })
return app;
},
output: "src/api",
propagate: true,
distribute: "packages/api",
};
export default config;
Returns IPropagation
typed instance instead of throwing exception.
When you configure propagate property of nestia.config.ts
file, all of SDK functions generated by @nestia/sdk
will perform propagation mode. The propagation mode means that never throwing exception (HttpError
) even when response status code is not 200 (or 201), but just returning the IPropagation
typed object, which can specify its body data type through discriminated union determined by status code.
Looking at below code tabs one by one, then you may understand exactly, what the propagation mode is. As you can see from below example code, @TypedException()
decorator function can be utilized to define the failure type with specific status code. Also, if returned status code is out of pre-defined, the IPropagation.data
type would be automatically casted to unknown
type.
import core from "@nestia/core";
import { Controller } from "@nestjs/common";
import typia, { tags } from "typia";
import { IBbsArticle } from "@api/lib/structures/IBbsArticle";
@Controller("bbs/articles/:section")
export class BbsArticlesController {
/**
* Update an article.
*
* @param section Section code
* @param id Target article ID
* @param input Content to update
* @returns Updated content
*/
@core.TypedException<TypeGuardError.IProps>(400)
@core.TypedRoute.Put(":id")
public async update(
@core.TypedParam("section") section: string,
@core.TypedParam("id") id: string & tags.Format<"uuid">,
@core.TypedBody() input: IBbsArticle.IStore,
): Promise<IBbsArticle> {
return {
...typia.random<IBbsArticle>(),
id,
section,
...input,
};
}
}
CLI Arguments
npx nestia sdk
npx nestia sdk --config nestia2.config.ts
npx nestia sdk --project tsconfig2.json
npx nestia sdk --config nestia3.config.ts --project tsconfig3.tsconfig.json
If you have a special configuration file that its file name is not nestia.config.ts
or the configuration file is not placed on the root directory of the project, you can specify it with --config
option like npx nestia sdk --config another.config.ts
.
Also, if you have a special tsconfig.json
file or the project file is not located in the root directory of the project, you can specify it with --project
argument like npx nestia sdk --project another.tsconfig.json
, too.
Comment Tags
Hiding
If you want to hide some API endpoints from the SDK library, write a comment tag to the controller method.
@deprepcated
: warning from IDE@internal
: hide fromd.ts
files@ignore
: actually ignore, so that not even generated
At first, @deprecated
does not hide target API from the SDK library, but just mark as deprecated. In that case, IDE will warn to the SDK users. In such reason, @deprecated
comment tag is useful for legacy API endpoints that would be removed in the future.
The second @internal
tag also does not hide from the SDK library, but it would be disappeared from d.ts
files, so that client developers can't identify the API. Therefore, @internal
tag is useful for some API endpoints that would be used only in the backend server for testing or debugging purpose.
The last @ignore
tag is the most powerful one. If you write @ignore
tag to the controller method, the SDK library would never generate the API endpoint at all. In that case, client developers can't use the API endpoint even if they know the API endpoint path and method. It is useful for some endpoints that are not supported in the SDK library.
import { Controller } from "@nestjs/common";
import { IBbsArticle } from "@api/lib/structures/IBbsArticle";
@Controller("bbs/articles")
export class BbsArticlesController {
/**
* Store an article.
*
* @param input Content to store
* @returns Newly archived article
* @deprecated
*/
public async create(
@TypedBody() input: IBbsArticle.IStore,
): Promise<IBbsArticle> {
...
}
/**
* @internal
*/
public async update(
@TypedParam("id") id: string & tags.Format<"uuid">,
@TypedBody() input: IBbsArticle.IUpdate,
): Promise<void> {
...
}
/**
* @ignore
*/
public async erase(
@TypedParam("id") id: string & tags.Format<"uuid">,
): Promise<void> {
...
}
}
Headers
Also, SDK library of @nestia/sdk
supports special comment tags configuring client headers.
@setHeader
@assignHeaders
At first, @setHeader {accessor}
configures only one header property. It reads special value of response body data with the accessor
, and configures the special value to client header with last accessor key. In the below example case, key of authorization.token
would be token
, and key of authorization.timeout
would be timeout
.
The other one @assignHeaders
overwrites every property values to the client headers, with special instance of response body data with accessor
. In the below example case, every properties in IShoppingCustomer.IActivated["authorization"]
would be assigned to the clinet headers.
If you're confused, read example codes of below, clicking each tabs.
import { Controller } from "@nestjs/common";
@Controller("shoppings/consumers/authenticate")
export class ShoppingConsumerAuthenticateController {
/**
* @setHeader authorization.token token
* @setHeader authorization.timeout timeout
*/
@TypedRoute.Post("join")
public join(
@TypedBody() input: IShoppingConsumer.IJoin
): Promise<IShoppingConsumer.IActivated>;
/**
* @assignHeaders authorization
*/
@TypedRoute.Post("login")
public login(
@TypedBody() inpu: IShoppingConsumer.ILogin
): Promise<IShoppingConsumer.IActivated>;
}
Distribution
import { INestiaConfig } from "@nestia/sdk";
import { NestFactory } from "@nestjs/core";
// import { FastifyAdapter } from "@nestjs/platform-fastify";
import { YourModule } from "./src/YourModule";
const NESTIA_CONFIG: INestiaConfig = {
input: async () => {
const app = await NestFactory.create(YourModule);
// const app = await NestFactory.create(YourModule, new FastifyAdapter());
// app.setGlobalPrefix("api");
// app.enableVersioning({
// type: VersioningType.URI,
// prefix: "v",
// })
return app;
},
output: "src/api",
distribute: "packages/api",
};
export default NESTIA_CONFIG;
The best to way to distributing SDK library is just publishing as an NPM module.
Configure distribute
property of nestia.config.ts
file, and run npx nestia sdk
command. After that, distribution environments would be automatically composed with SDK library generation. At last, move to the packages/api
directory, and run npm run deploy
command for publishing.
From now on, client developers can use the SDK library just by using npm install
command.
cd packages/api
npm run deploy
Of course, before publishing the NPM module, you've to customize some configurations like package name. Initial name of the distribution envirionments is @ORGANIZATION/PROJECT-api
, but you must change the package name of yours, isn't it?
Also, if your SDK library utilize special alias paths
, you also need to customize tsconfig.json
file, too. Reading below example package.json
and tsconfig.json
files generated by nestia
, consider which features to customize.
{
"name": "@ORGANIZATION/PROJECT-api",
"version": "0.1.0",
"description": "SDK library generated by Nestia",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"scripts": {
"build": "npm run build:sdk && npm run compile",
"build:sdk": "rimraf ../../src/api/functional && cd ../.. && npx nestia sdk && cd packages/api",
"compile": "rimraf lib && tsc",
"deploy": "npm run build && npm publish"
},
"repository": {
"type": "git",
"url": "https://github.com/samchon/nestia"
},
"author": "Jeongho Nam",
"license": "MIT",
"bugs": {
"url": "https://github.com/samchon/nestia/issues"
},
"homepage": "https://nestia.io",
"devDependencies": {
"rimraf": "^5.0.0",
"typescript": "^5.4.2",
"ts-patch": "^3.1.0"
},
"dependencies": {
"@nestia/fetcher": "^3.4.1",
"typia": "^6.4.0"
},
"files": [
"lib",
"package.json",
"README.md"
]
}