Skip to Content
RPC for NestJS

RPC for NestJS

Install dependencies

npm i vovk-client
npm i vovk-cli@draft -D

Expose OpenAPI spec in your NestJS app

At this case we expose it at /api-ref endpoint and Swagger UI at /docs.

src/main.ts
import { NestFactory } from '@nestjs/core'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { AppModule } from './app.module'; async function bootstrap() { const appOptions = { cors: true }; const app = await NestFactory.create(AppModule, appOptions); app.setGlobalPrefix('api'); const options = new DocumentBuilder() .setTitle('NestJS Example App') .setDescription('The API description') .setVersion('1.0') .addBearerAuth() .build(); const document = SwaggerModule.createDocument(app, options); app.getHttpAdapter().get('/api-ref', (req, res) => { res.json(document); }); SwaggerModule.setup('/docs', app, document); await app.listen(3000); } bootstrap() .catch((err) => { console.log(err); });

Use OpenAPI decorators from @nestjs/swagger 

import { Controller, Post, Body, Query, Param, HttpStatus } from '@nestjs/common'; import { ApiOperation, ApiResponse, ApiParam, ApiBody, ApiQuery, ApiExtraModels } from '@nestjs/swagger'; import { CreateUserDto, QueryParamsDto, UserResponseDto } from './dto/user.dto'; @Controller('users') @ApiExtraModels(UserResponseDto) // Ensures the DTO appears in schemas export class UserController { @Post(':organizationId') @ApiOperation({ summary: 'Create a new user' }) @ApiParam({ name: 'organizationId', type: 'string', description: 'Organization ID', }) @ApiBody({ type: CreateUserDto, description: 'User creation data' }) @ApiQuery({ type: QueryParamsDto, required: false }) @ApiResponse({ status: HttpStatus.CREATED, description: 'User successfully created', type: UserResponseDto, // This ensures it appears in components/schemas }) async createUser( @Param('organizationId') organizationId: string, @Body() createUserDto: CreateUserDto, @Query() queryParams: QueryParamsDto ): Promise<UserResponseDto> { // ... } }

Add generate script to package.json and run it

The NestJS server should be running at this moment on http://localhost:3000 . Set --openapi-root-url to your server URL if it’s different.

package.json
{ "scripts": { // ... "start:dev": "nest start --watch", "generate": "vovk g --openapi=http://localhost:3000/api-ref --openapi-get-module-name=nestjs-operation-id --openapi-get-method-name=nestjs-operation-id --openapi-root-url=http://localhost:3000 --openapi-fallback .openapi-cache/openapi.json" } }
npm run generate

You can optionally run start:dev and generate scripts (with --watch flag) in parallel using concurrently package.

package.json
{ "scripts": { // ... "dev": "concurrently \"npm run start:dev\" \"sleep 5 && npm run generate -- --watch\" --kill-others" } }
npm run dev

For more flexibility it’s recommended to create a config file (see below).

As you may notice, --openapi-get-module-name and --openapi-get-method-name are set to nestjs-operation-id. This strategy assumes that operationId in your OpenAPI spec is defined as XxxController_methodName, e.g. UserController_createUser. This is the default behavior of @nestjs/swagger package. XxxController is turned into an RPC module name XxxRPC and methodName is turned into a method name as is. If you want to change this behavior, you can use config file to define getModuleName and getMethodName functions manually.

Import and use

Import the resulting client from any fetch-enabled environment:

import { UserRPC } from 'vovk-client'; const user = await UserRPC.createUser({ body: { name: 'John Doe', email: 'john.doe@example.com' }, query: { page: 1, limit: 10 }, params: { organizationId: 'org123' } });

Create config file

If the flags are too long, or if you want to use additioannal Vovk.ts features, create a config file in the project root:

vovk.config.mjs
/** @type {import('vovk-cli').VovkConfig} */ const config = { generatorConfig: { nest: { openAPIMixin: { source: { url: 'http://localhost:3000/api-ref', fallback: '.openapi-cache/openapi.json', }, apiRoot: 'http://localhost:3000', getModuleName: 'nestjs-operation-id', getMethodName: 'nestjs-operation-id', }, } } }; export default config;

At this case the generate script can be simplified:

package.json
{ "scripts": { // ... "generate": "vovk g" } }

Configure fetcher

In order to configure authorization headers, add custom options to the generated client, or transform the response, you can create a custom fetcher (requires config file).

import { UserRPC } from 'vovk-client'; await UserRPC.updateUser({ // ... successMessage: 'User updated successfully', useAuthorization: true, somethingCustom: 'customValue', });

Enable client-side validation

The client-side validation isn’t enabled by default. For a NestJS app there are 2 options:

vovk-ajv

npm i vovk-ajv@draft
vovk.config.mjs
/** @type {import('vovk').VovkConfig} */ const config = { generatorConfig: { imports: { validateOnClient: 'vovk-ajv', }, }, }; export default config;

For more information see the vovk-ajv section.

vovk-dto/validateOnClient

npm i vovk-dto@draft
vovk.config.mjs
/** @type {import('vovk').VovkConfig} */ const config = { generatorConfig: { imports: { validateOnClient: 'vovk-dto/validateOnClient', }, }, }; export default config;

For more information see the vovk-dto/validateOnClient section.

Function calling

As any other RPC module, the generated NestJS RPC modules can be turned into LLM tools, invoking the server methods thru HTTP protocol.

For more information see the function calling page.

Last updated on