NestJS Introductie
NestJS Introductie
Nest (NestJS) is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with and fully supports TypeScript (yet still enables developers to code in pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).
$ npm i -g @nestjs/cli
$ nest new project-name
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
const app = await NestFactory.create<NestExpressApplication>(AppModule);
Navigeer met je browser naar http://localhost:3000/.
Je krijgt nu Hello World! te zien.
NestJS Introductie
"Controllers are responsible for handling incoming requests and returning responses to the client."
import { Controller, Get } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get()
findAll(): string {
return 'This action returns all cats';
}
}
De response code is altijd 200, tenzij
Routing
import { Controller, Get, Req } from '@nestjs/common';
import { Request } from 'express';
@Controller('cats')
export class CatsController {
@Get('all')
findAll(@Req() request: Request): string {
return 'This action returns all cats';
}
}
https://localhost:3000/cats/all
Route Parameters
@Get(':id')
findOne(@Param() params): string {
console.log(params.id);
return `This action returns a #${params.id} cat`;
}
Voeg de @Param decorator toe voor alle parameters of voor individuele parameters
@Get(':id')
findOne(@Param('id') id: string): string {
return `This action returns a #${id} cat`;
}
HTTP Methods: @GET, @POST, @PUT, @DELETE
import { Controller, Get, Post } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Post()
create(): string {
return 'This action adds a new cat';
}
@Get()
findAll(): string {
return 'This action returns all cats';
}
}
@Body
@Post()
async create(@Body() createCatDto: CreateCatDto) {
return 'This action adds a new cat';
}
Maak een Data Transfer Object aan of DTO in een afzonderlijke dto folder. Geef het de filename create-cat.dto.ts.
export class CreateCatDto {
name: string;
age: number;
breed: string;
}
Status Code
@Post()
@HttpCode(204)
create() {
return 'This action adds a new cat';
}
Asynchrone Calls -> Out Of The Box
@Get()
async findAll(): Promise<any[]> {
return [];
}
Voeg Controllers toe aan je AppModule
import { Module } from '@nestjs/common';
import { CatsController } from './cats/cats.controller';
@Module({
controllers: [CatsController],
})
export class AppModule {}
"Many of the basic Nest classes may be treated as a provider – services, repositories, factories, helpers, and so on. The main idea of a provider is that it can be injected as dependency; this means objects can create various relationships with each other, and the function of "wiring up" instances of objects can largely be delegated to the Nest runtime system."
Providers zijn plain JavaScript classes die worden gedeclareerd in een module.
import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface';
@Injectable()
export class CatsService {
private readonly cats: Cat[] = [];
create(cat: Cat) {
this.cats.push(cat);
}
findAll(): Cat[] {
return this.cats;
}
}
export interface Cat {
name: string;
age: number;
breed: string;
}
import { Controller, Get, Post, Body } from '@nestjs/common';
import { CreateCatDto } from './dto/create-cat.dto';
import { CatsService } from './cats.service';
import { Cat } from './interfaces/cat.interface';
@Controller('cats')
export class CatsController {
constructor(private catsService: CatsService) {}
@Post()
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
@Get()
async findAll(): Promise<Cat[]> {
return this.catsService.findAll();
}
}
De CatsService wordt geïnjecteerd door de constructor. In dit geval is de catService private.
import { Module } from '@nestjs/common';
import { CatsController } from './cats/cats.controller';
import { CatsService } from './cats/cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class AppModule {}
A module is a class annotated with a @Module() decorator. The @Module() decorator provides metadata that Nest makes use of to organize the application structure.
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class CatsModule {}
cats/cats.module.ts
Maak een service aan met de CLI: nest g module cats
import { Module } from '@nestjs/common';
import { CatsModule } from './cats/cats.module';
@Module({
imports: [CatsModule],
})
export class AppModule {}
app.module.ts
Deel een service provider met andere modules door ze te exporteren
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
exports: [CatsService]
})
export class CatsModule {}
Elke module die CatModule importeert, heeft toegang tot de CatsService.
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log('Request...');
next();
}
}
logger.middleware.ts
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';
@Module({
imports: [CatsModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes('cats');
}
}
Toepassen middleware in app.module.ts
Gebruik forRoutes om nog specifieker middleware toe te passen:
import { Module, NestModule, RequestMethod, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';
@Module({
imports: [CatsModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes({ path: 'cats', method: RequestMethod.GET });
}
}
Of gebruik een Controller in de forRoutes method:
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';
import { CatsController } from './cats/cats.controller.ts';
@Module({
imports: [CatsModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes(CatsController);
}
}
Meerdere middleware kan je toevoegen in de apply() method:
consumer.apply(cors(), helmet(), logger).forRoutes(CatsController);
A pipe is a class annotated with the @Injectable() decorator. Pipes should implement the PipeTransform interface.
Er zijn een aantal out-of-the-box types:
@Get(':id')
async findOne(@Param('id', ParseIntPipe) id: number) {
return this.catsService.findOne(id);
}
GET localhost:3000/abc
{
"statusCode": 400,
"message": "Validation failed (numeric string is expected)",
"error": "Bad Request"
}
$ npm i --save class-validator class-transformer
of
$ yarn add class-validator class-transformer
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
Neem volgende endpoint:
@Post()
create(@Body() createUserDto: CreateUserDto) {
return 'This action adds a new user';
}
import { IsEmail, IsNotEmpty } from 'class-validator';
export class CreateUserDto {
@IsEmail()
email: string;
@IsNotEmpty()
password: string;
}
Valideer dto met de decorators van 'class-validator':
Wanneer er met bovenstaande regels een foute e-mail wordt ingevoerd, krijgen we volgende melding:
{
"statusCode": 400,
"error": "Bad Request",
"message": ["email must be an email"]
}
Meer info over validation pipe: https://docs.nestjs.com/techniques/validation
Nest comes with a built-in exceptions layer which is responsible for processing all unhandled exceptions across an application. When an exception is not handled by your application code, it is caught by this layer, which then automatically sends an appropriate user-friendly response.
Standaard is er een built-in global exception handler van het type HttpException. Deze genereert volgend JSON response:
{
"statusCode": 500,
"message": "Internal server error"
}
We kunnen de HTTPException klasse ook gebruiken in een controller om specifieke fouten te melden:
@Get()
async findAll() {
throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
}
De HTTPException neemt twee verplichte argumenten:
De client zal volgende response teruggeven op bovenstaande endpoint:
{
"statusCode": 403,
"message": "Forbidden"
}
Built-in HTTP exceptions zijn