đź“– Guide Documents
Generators
S/W Development Kit

Outline

Terminal
npx nestia sdk
npx nestia sdk --config nestia.config.ts --project tsconfig.json

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. The newly generated SDK library would be composed with DTO and fetch functions with type definitions following your NestJS server.

With the SDK library, 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 when using Swagger Documents?

SDK

Left is NestJS server code, and right is client (frontend) code utilizing SDK

Configuration

nestia.config.ts

nestia.config.ts
import { INestiaConfig } from "@nestia/sdk";
import { NestFactory } from "@nestjs/core";
// import { FastifyAdaptor } 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 FastifyAdaptor());
    // 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 classes
  • output: 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.

Input Pattern

nestia.config.ts supports alternative options 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.

nestia.config.ts
import { INestiaConfig } from "@nestia/sdk";
 
const NESTIA_CONFIG: INestiaConfig = {
  input: ["src/controllers", "src/fake/controllers", "src/test/controllers"],
  output: "src/api",
  distribute: "packages/api",
};
export default NESTIA_CONFIG;

Clone Mode

nestia.config.ts
import { INestiaConfig } from "@nestia/sdk";
import { NestFactory } from "@nestjs/core";
// import { FastifyAdaptor } 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 FastifyAdaptor());
    // 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

nestia.config.ts
import { INestiaConfig } from "@nestia/sdk";
import { NestFactory } from "@nestjs/core";
// import { FastifyAdaptor } 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 FastifyAdaptor());
    // 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.

src/controllers/BbsArticlesController.ts
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,
    };
  }
}

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 from d.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.

BbsArticlesController.ts
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.

ShoppingConsumerAuthenticateController.ts
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

nestia.config.ts
import { INestiaConfig } from "@nestia/sdk";
import { NestFactory } from "@nestjs/core";
// import { FastifyAdaptor } 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 FastifyAdaptor());
    // 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.

deploy.sh
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.

package.json
{
  "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": "^2.3.4",
    "typia": "^6.0.0"
  },
  "files": [
    "lib",
    "package.json",
    "README.md"
  ]
}