-
Create a Serializer Interceptor
Createserializer.interceptor.ts
to handle response serialization.// src/utils/serializer.interceptor.ts import { CallHandler, ExecutionContext, Injectable, NestInterceptor, } from '@nestjs/common'; import { Observable } from 'rxjs'; @Injectable() export class SerializerInterceptor implements NestInterceptor { intercept( context: ExecutionContext, next: CallHandler, ): Observable<any> | Promise<Observable<unknown>> { return next.handle().pipe(); } }
-
Define Validation Options
Createvalidation-options.util.ts
for global validation settings.// src/utils/validation-options.util.ts import { HttpException, HttpStatus, ValidationError, ValidationPipeOptions, } from '@nestjs/common'; const validationOptionsUtil: ValidationPipeOptions = { transform: true, whitelist: true, errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY, exceptionFactory: (errors: ValidationError[]) => new HttpException( { status: HttpStatus.UNPROCESSABLE_ENTITY, errors: errors.reduce( (accumulator, currentValue) => ({ ...accumulator, [currentValue.property]: Object.values( currentValue.constraints, ).join(', '), }), {}, ), }, HttpStatus.UNPROCESSABLE_ENTITY, ), }; export default validationOptionsUtil;
-
Set Up Bootstrap Functionality
Createbootstrap.util.ts
to initialize the application.// src/utils/bootstrap.util.ts import { INestApplication, ValidationPipe, VersioningType, } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { SerializerInterceptor } from './serializer.interceptor'; import validationOptionsUtil from './validation-options.util'; export const documentationBuilder = ( app: INestApplication, configService: ConfigService, ) => { const config = new DocumentBuilder() .addBearerAuth() .setTitle(configService.get('app.name')) .setDescription('The Shinobi API description') .setVersion('1') .build(); const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup('docs', app, document); }; export const createApplication = (app: INestApplication) => { app.enableShutdownHooks(); app.enableVersioning({ type: VersioningType.URI, }); app.useGlobalInterceptors(new SerializerInterceptor()); app.useGlobalPipes(new ValidationPipe(validationOptionsUtil)); return app; };
-
Integrate Bootstrap in Main File
Use the bootstrap functions inmain.ts
.// src/main.ts import { NestFactory } from '@nestjs/core'; import { AppModule } from './modules/app/app.module'; import { ConfigService } from '@nestjs/config'; import { Logger } from '@nestjs/common'; import { createApplication, documentationBuilder, } from './utils/bootstrap.util'; async function bootstrap() { const log = new Logger(bootstrap.name); const app = await NestFactory.create(AppModule); const configService = app.get(ConfigService); createApplication(app); documentationBuilder(app, configService); const PORT = configService.get('app.port') || 8000; await app.listen(PORT); log.log( `NestJS Series (${process.env.npm_package_version}) start on port ${PORT}`, ); } bootstrap();
-
Define Strong Password Config
Createstrong-password.config.ts
for password strength settings.// src/modules/auth/strong-password.config.ts export const strongPasswordConfig = { minLength: 8, minLowercase: 1, minNumbers: 1, minSymbols: 1, minUppercase: 1, };
-
Create DTOs for Authentication
Define various data transfer objects (DTOs) for the login and registration processes.- Login DTO
// src/modules/auth/dtos/login.dto.ts import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; import { IsEmail, IsString, IsStrongPassword } from 'class-validator'; import { strongPasswordConfig } from 'src/modules/auth/strong-password.config'; export class LoginDto { @ApiProperty({ example: '[email protected]' }) @IsEmail() @Transform(({ value }) => typeof value === 'string' ? value.toLowerCase() : value, ) email: string; @ApiProperty({ example: 'StrongP@ssword123' }) @IsString() @IsStrongPassword(strongPasswordConfig) password: string; }
- Register DTO
// src/modules/auth/dtos/register.dto.ts import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; import { IsEmail, IsNotEmpty, IsString, IsStrongPassword, } from 'class-validator'; import { strongPasswordConfig } from 'src/modules/auth/strong-password.config'; export class RegisterDto { @ApiProperty({ example: '[email protected]' }) @IsEmail() @Transform(({ value }) => typeof value === 'string' ? value.toLowerCase() : value, ) email: string; @ApiProperty({ example: 'StrongP@ssword123' }) @IsString() @IsStrongPassword(strongPasswordConfig) password: string; @ApiProperty({ example: 'Faqih' }) @IsString() @IsNotEmpty() first_name: string; @ApiProperty({ example: 'Pratama Muhti' }) @IsString() @IsNotEmpty() last_name: string; }
- Email Verify DTO
// src/modules/auth/dtos/email-verify.dto.ts import { ApiProperty } from '@nestjs/swagger'; import { IsString } from 'class-validator'; export class EmailVerifyDto { @ApiProperty({ example: 'vhsbdjsdfsd-dfmsdfjsd-sdfnsdk' }) @IsString() verify_token: string; }
- Reset Password DTO
// src/modules/auth/dtos/reset-password.dto.ts import { ApiProperty } from '@nestjs/swagger'; import { IsString, IsStrongPassword } from 'class-validator'; import { strongPasswordConfig } from 'src/modules/auth/strong-password.config'; export class ResetPasswordDto { @ApiProperty({ example: 'StrongP@ssword123' }) @IsString() @IsStrongPassword(strongPasswordConfig) password: string; @ApiProperty({ example: 'vhsbdjsdfsd-dfmsdfjsd-sdfnsdk' }) @IsString() reset_token: string; }
- Send Verification Email DTO
// src/modules/auth/dtos/send-verify-mail.dto.ts import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; import { IsEmail } from 'class-validator'; export class SendVerifyMailDto { @ApiProperty({ example: '[email protected]' }) @IsEmail() @Transform(({ value }) => typeof value === 'string' ? value.toLowerCase() : value, ) email: string; }
-
Create Email Service
Define the service to handle email operations.// src/modules/auth/email.service.ts import { Injectable } from '@nestjs/common'; @Injectable() export class EmailService { constructor() {} }
-
Create Email Controller
Define the controller to manage authentication-related routes.// src/modules/auth/email.controller.ts import { Body, Controller, HttpCode, HttpStatus, Post } from '@nestjs/common'; import { EmailService } from './email.service'; import { RegisterDto } from './dtos/register.dto'; import { EmailVerifyDto } from './dtos/email-verify.dto'; import { LoginDto } from './dtos/login.dto'; import { SendVerifyMailDto } from './dtos/send-verify-mail.dto'; import { ApiAcceptedResponse, ApiCreatedResponse, ApiForbiddenResponse, ApiNoContentResponse, ApiNotFoundResponse, ApiOperation, ApiTags, } from '@nestjs/swagger'; @ApiTags('Auth Email') @Controller({ path: 'auth/email', version: '1', }) export class EmailController { constructor(private emailService: EmailService) {} @Post('/register') @ApiOperation({ summary: 'Register by email' }) @ApiCreatedResponse({ description : 'Registered successfully', }) @ApiForbiddenResponse({ description: 'Forbidden', }) async register(@Body() registerDto: RegisterDto) { return await this.emailService.register(registerDto); } @Post('/login') @ApiOperation({ summary: 'Login by email' }) @ApiAcceptedResponse({ description: 'Logged in successfully', }) @ApiNotFoundResponse({ description: 'Email or password not found', }) async login(@Body() loginDto: LoginDto) { return await this.emailService.login(loginDto); } @Post('/verify') @HttpCode(HttpStatus.NO_CONTENT) @ApiOperation({ summary: 'Verify email' }) @ApiNoContentResponse({ description: 'Email verified successfully', }) @ApiForbiddenResponse({ description: 'Email already verified', }) async verifyEmail(@Body() emailVerifyDto: EmailVerifyDto) { return await this.emailService.verifyEmail(emailVerifyDto); } @Post('/send-verify-email') @HttpCode(HttpStatus.NO_CONTENT) @ApiOperation({ summary: 'Send verification email' }) @ApiNoContentResponse({ description: 'Verification email sent', }) async sendVerifyEmail( @Body() sendVerifyMailDto: SendVerifyMailDto, ) { return await this.emailService.sendVerifyEmail(sendVerifyMailDto); } }
This summary provides a structured approach to setting up validation and controllers in a NestJS application. Each code snippet is categorized and annotated, making it easy to integrate into your existing project. If you have any further questions or need additional modifications, feel free to ask!