0.3.5 • Published 22 days ago

@voodoo-ts/nest v0.3.5

Weekly downloads
-
License
GPL-3.0-or-later
Repository
github
Last release
22 days ago

About

This integrates voodoo-ts into NestJS.

Getting started

Install

npm install @voodoo-ts/nest

General setup 🫡

First create an instance of voodoo-ts and export it, so it can be shared between modules

// voodoo.instance.ts

import {
  StringToBooleanValueTransformer,
  StringToNumberValueTransformer,
  TransformerInstance,
} from '@voodoo-ts/voodoo-ts';

export const voodoo = TransformerInstance.withDefaultProject({
  additionalValueTransformerFactories: [new StringToNumberValueTransformer(), new StringToBooleanValueTransformer()],
});

Validation

Use the validation pipeline

// main.ts

import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@voodoo-ts/nest';

import { AppModule } from './app.module';
import { voodoo } from './validation.instance';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe({ transformerInstance: voodoo }));
  await app.listen(3000);
}
bootstrap();

Now you can use voodoo-ts to validate @Body() and @Query() payloads as usual.

// app.constroller.ts

import { voodoo } from './validation.instance';

@voodoo.Dto()
class LoginDto {
  email!: string;
  password!: string;
}

@Controller()
export class AppController {
  /*
   * This the postHello method
   */
  @Post('/login')
  login(@Body() body: LoginDto): string {
    // ... stuff ...
    return `Hello ${body.email}`;
  }
}

This will work only for classes decorated as @Dto(). And for primitive data types if you specify the field name, e.g. @Query('foo') foo: number. Unlike the default validation pipeline this will throw errors if it encounters a type reference it can't validate.

Swagger

Create an instance of the SwaggerVoodoo class in your central voodoo.instance.ts, or whereever you like

// voodoo.instance.ts

import {
  StringToBooleanValueTransformer,
  StringToNumberValueTransformer,
  TransformerInstance,
} from '@voodoo-ts/voodoo-ts';

export const voodoo = TransformerInstance.withDefaultProject({
  additionalValueTransformerFactories: [new StringToNumberValueTransformer(), new StringToBooleanValueTransformer()],
});

export const { ApiModel, additionalModels } = new SwaggerVoodoo(voodoo).unwrap();

Now can use @ApiModel() to decorate Dto classes. This will ensure all properties are wrapped with @ApiProperty() and enrich their Swagger types and documentation. It's still recommended to enable the swagger-cli plugin from NestJS, but you should disable the class-validator shims

// nest-cli.json
{
  "$schema": "https://json.schemastore.org/nest-cli",
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "deleteOutDir": true,
    "plugins": [
      {
        "name": "@nestjs/swagger",
        "options": {
          "classValidatorShim": false,
          "introspectComments": true
        }
      }
    ]
  }
}

voodoo-swagger will automatically create classes for intersections, unions and partials. But you need to expose them to @nestjs/swagger. These classes will be tracked in additionalModels.

// main.ts

import { additionalModels } from './voodoo.instance.ts';

async function bootstrap() {
  // ...
  const document = SwaggerModule.createDocument(app, config, { extraModels: additionalModels });
  SwaggerModule.setup('api', app, document, { customCssUrl: '/theme.css' });
  // ...
}

Make sure everything is imported at this point.

Environment helper

Use the environment helper

// app.module.ts

import { Global, Module } from '@nestjs/common';
import { ConfigModule } from '@voodoo-ts/nest';
import { From } from '@voodoo-ts/voodoo-ts';

import { AppController } from './app.controller';
import { AppService } from './app.service';
import { voodoo } from './validation.instance';

@voodoo.Dto()
class Env {
  @From('HOME')
  home!: string;
}

// If you don't want to import the ConfigModule in each module
@Global()
@Module({
  imports: [ConfigModule.register(transformer, Env)],
  exports: [ConfigModule],
})
export class GlobalModule {}

@Module({
  imports: [GlobalModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
0.3.5

22 days ago

0.3.4

2 months ago

0.3.3

2 months ago

0.3.2

4 months ago

0.3.1

6 months ago

0.3.0

6 months ago

0.2.0

6 months ago

0.1.0

6 months ago

0.0.2

6 months ago

0.0.1-beta.2

10 months ago

0.0.1-beta.0

10 months ago