NestJS

backend out of the box

https://github.com/valentinkononov

http://kononov.space/

 

@ValentinKononov

 

Developer, Speaker

 

 

Everyday life

@ValentinKononov

I will not write a bad code

Pure NodeJS

@ValentinKononov

// server.js

const express = require("express");
const app = express();
const PORT = process.env.PORT = 3000;

let router = express.Router();

router.get('/',function(req,res){
  res.json({'message' : 'Ping Successfull'});
});

app.use('/api',router);

app.listen(PORT,function(){
  console.log('Server is running at PORT:',PORT);
});

Event Loop

@ValentinKononov

What is better app

@ValentinKononov

  • Works as expected
  • Fast enough
  • There are users
  • Code is readable
  • Can modify App without re-writing
  • Has adequate tests
  • You don't suffer with it

Make better apps

@ValentinKononov

Why we choose NestJS

@ValentinKononov

  • Declarative approach

  • Feature Modules

  • Solid structure

  • Readable for Frontenders and Backenders

  • Node inside

  • Number of out-of-the-box features

Readability. Samples

@ValentinKononov

NestJS is coming

@Controller('user')
@UseGuards(AuthGuard('jwt'))
@UseGuards(RoleGuard)
export class UserController {
  constructor(private readonly service: UserService) {}

  @Role('admin')
  @Post()
  async create(@Body() user: User) {
    return await this.service.create(user);
  }

  @Role('user')
  @Get(':id')
  async getById(@Param() id: number) {
    return await this.service.getById(id);
  }
}

@ValentinKononov

Modules

@Module({
  import: [...],
  export: [...],
  controllers: [
    UserController,
  ],
  providers: [
    UserService,
  ],
})
export class AppModule {}

@ValentinKononov

main.ts

async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    cors: true,
  });
  await app.listen(3100);
}
bootstrap();

@ValentinKononov

@ValentinKononov

Modularity and Structure

Layered Architecture

@ValentinKononov

Shared Modules

@ValentinKononov

ReactJS

Frontend Code

NestJS

Backend Code

Shared

Library

DTO

Validation

Check Production Build

Demo

@ValentinKononov

Framework Parts

@ValentinKononov

Modules

Controllers

Routing

Dependency Injection

Middleware

Pipes

Guards

Exceptions

Interceptors

Dependency Injection

@ValentinKononov

Dependency Injection

@Injectable({
  scope: Scope.DEFAULT,
})
export class UserRepository {...}

@ValentinKononov

@Injectable()
export class UserService {
  constructor(private readonly repository: UserRepository) {}
}
@Module({
  controllers: [UserController],
  providers: [UserService, UserRepository],
  exports: [UserService, UserRepository],
})
export class UserModule {}

DI Scopes

export declare enum Scope {
    /**
     * One for the application
     */
    DEFAULT = 0,
    /**
     * New instance each time
     */
    TRANSIENT = 1,
    /**
     * A new instance each request
     */
    REQUEST = 2
}

@ValentinKononov

Request Handling

@ValentinKononov

Controller

  @Post()
  async create(
    @Body() user: User, 
    @Req() req, 
    @Res() res,
  ) {
    return await this.service.create(user);
  }

@ValentinKononov

  • Access to Request and Response
  • Validation and Transformation Happened
  • Types

Middleware

@Injectable()
export class CustomMiddleware implements NestMiddleware {
  use(
      req: Request, 
      res: Response, 
      next: () => void) {
        Logger.debug('Middleware');
        next();
  }
}

@ValentinKononov

  • Access to Request and Response
  • Can end request handling
  • Equivalent to express middleware

Guard

@Injectable()
export class AdminGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean | Promise<boolean> {
    Logger.debug('Guard')
    const request = context.switchToHttp().getRequest();
    request.user.role === 'admin';
  }
}

@ValentinKononov

  • Simple CanActivate handler
  • Specific area of usage
    • authentication
    • role check
    • validation
@UseGuards(AdminGuard)
export class Controller {}

Interceptor

@Injectable()
export class PerformanceInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    Logger.debug('Interceptor started')
    const now = Date.now();
    return next
      .handle()
      .pipe(
        tap(() => {
          Logger.debug('Interceptor completed')
          Logger.log(`${this.requestTag} completed in: ${Date.now() - now}ms`)
        }),
      );
  }
}

@ValentinKononov

  • Extra logic
  • Transformation
  • Caching
🤝
1

Pipe

@Injectable()
export class LogPipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    Logger.debug('Pipe');
    return value;
  }

}

@ValentinKononov

  • Transformation
  • Validation
  • Out-of-the-box pipes
    • ValidationPipe
    • DefaultValuePipe
    • ....
@UsePipes(new LogPipe())
export class Controller {}

Request Pipeline

@ValentinKononov

Middleware

Pipes

Guards

Controller

Interceptor Start

Interceptor End

Exception Filter

Scenarios

@ValentinKononov

Scheduler

@ValentinKononov

Schedule Service

@ValentinKononov

@Injectable()
export class EveryMinuteService {
  @Cron(CronExpression.EVERY_5_SECONDS)
  handleEveryMinuteJob() {
    Logger.debug(`I was called at: ${new Date()}`);
  }
}

Think about scaling

Swagger

@ValentinKononov

Swagger API

@ValentinKononov

Swagger Integration

@ValentinKononov

import { ApiProperty } from '@nestjs/swagger';

export class Email {
  @ApiProperty()
  email: string;
}
@ApiTags('auth')
@Controller('auth')
export class AuthController {...}

Swagger Integration

@ValentinKononov

async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    cors: true,
  });

  SwaggerModule.setup('swagger', app, 
     SwaggerModule.createDocument(app, { ... }));

  await app.listen(config.api.port);
}
bootstrap();

Validation

@ValentinKononov

 Class-Validator

@ValentinKononov

export class UserDto {
    @IsPositive()
    id: number;

    @IsString()
    name: string;

    @IsEmail()
    email: string;

    @IsDate()
    birthday: Date;
}

@UsePipes(new ValidationPipe({ transform: true }))
@Controller('users')
export class UserController {...}

Microservices

JWT Auth

Life Cycle Events

Web Sockets

GraphQL

CQRS

Template Rendering

Performance - Fastify

@ValentinKononov

Other Feartures

@ValentinKononov

Template Rendering

npm install --save hbs
async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.setBaseViewsDir(join(__dirname, '..', 'views'));
  app.setViewEngine('hbs');

  await app.listen(3000);
}
bootstrap();

@ValentinKononov

Handlebar Usage

<html>
  <body>
    {{ message }}
  </body>
</html>
@Controller()
export class AppController {
  @Get()
  @Render('index')
  root() {
    return { message: 'Test Handlebar templates' };
  }
}

@ValentinKononov

MVC

@ValentinKononov

Platforms Support

npm i --save @nestjs/platform-fastify
async function bootstrap() {
  const app = await NestFactory
    .create<NestFastifyApplication>(
      AppModule,
      new FastifyAdapter()
  );
  await app.listen(3000);
}
bootstrap();

Think about platform specific actions like redirect

Fastify

@ValentinKononov

@ValentinKononov

API Template

  • Clone and modify
  • Number of samples
  • Configuration
  • JWT Auth
  • Layered Architecture

NESTER - Backend Template

github.com/valentinkononov/nester

@ValentinKononov

@ValentinKononov

@ValentinKononov

  • Plenty of Features
  • Natural for UI and Backend developers
  • Readable and Editable Code
  • Solid Code Structure
  • Enjoyable!

Conclusion

@ValentinKononov

See Ya

Made with Slides.com