Khởi đầu từ bài viết này, chúng ta sẽ bắt đầu tìm hiểu về NestJS - một trong những framework nổi tiếng nhất dựa trên Node.js. NestJS kết hợp giữa TypeScript, tư duy hướng đối tượng và hàm, cung cấp một cách hiệu quả và có thể mở rộng để xây dựng ứng dụng Node.js. So với các framework như Express hay Koa, NestJS tỏ ra vượt trội hơn hẳn về mặt架构.
NestJS sử dụng mạnh mẽ mô hình IOC (Control反转), giúp tách biệt việc quản lý đối tượng và phụ thuộc khỏi code ứng dụng, giao cho container xử lý. Điều này làm giảm độ phức tạp của code, đồng thời cải thiện tính dễ bảo trì và tái sử dụng. Trong IOC, container xử lý việc tạo và quản lý phụ thuộc của các đối tượng, trong khi Dependency Injection (DI) là một cách thực hiện IOC, giúp cung cấp các đối tượng phụ thuộc vào class một cách全自动.
NestJS không chỉ dừng lại ở đó, mà còn cung cấp các khái niệm như middlewares, interceptors, filters, exceptions, pipes, và các phương pháp xác thực, đều đáng để chúng ta nghiên cứu và học hỏi. Học NestJS không chỉ là học cách sử dụng, mà còn là lĩnh hội tư duy thiết kế của nó, như架构 cơ bản, nguyên lý IOC, v.v...
Cài đặt
# 1. Tạo repository từ git
➜ git clone https://github.com/nestjs/typescript-starter.git nestjs-starter
# 2. Cài đặt CLI của NestJS
➜ npm i -g @nestjs/cli
➜ nest new nestjs-template
Entry Điểm Của Ứng Dụng
// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
NestJS có thể kết hợp với bất kỳ framework HTTP nào, mặc định là Express và Fastify. Dưới đây là cách kết hợp với Express:
import type { NestExpressApplication } from '@nestjs/platform-express';
const app = await NestFactory.create<NestExpressApplication>(AppModule);
Module
Một module là điểm bắt đầu của ứng dụng NestJS, ít nhất phải có một module. Module cung cấp các metadata cho ứng dụng, thường được chia theo chức năng.
Module có 4屬 tính重要作用:
- controllers: Cung cấp các controller xử lý các yêu cầu HTTP
- providers: Cung cấp các data có thể inject được, như các service
- imports: Nhập các module phụ thuộc
- exports: Expose các phần tử trong module để các module khác có thể sử dụng
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
exports: [AppService],
imports: [MockModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
###.Controllers
Controller được sử dụng để định nghĩa các route xử lý yêu cầu HTTP. Chúng thường được_DECORATE với @Controller.
Ví dụ:
@Controller('cat')
export class CatController {
@Get()
findAll() {}
}
###.Providers
Providers cung cấp các metadata có thể inject, bao gồm các class, hàm, hoặc các giá trị. Các class này thường được_DECORATE với @Injectable.
// Cung cấp giá trị chuỗi
{
provide: 'APP_NAME',
useValue: 'nestjs-template',
}
// Cung cấp class
@Module({
providers: [Test],
})
// Cung cấp实例
{
provide: Test,
useValue: new Test(),
}
###.Imports & Exports
Imports được sử dụng đểนำ các module phụ thuộc vào, còn exports thì cho phép các module khác sử dụng các provider trong module hiện tại.
import { UserModule } from './user/user.module';
@Module({
imports: [UserModule],
})
export class AppModule {}
Dynamic Modules
Dynamic modules giúp chia sẻ cấu hình giữa các module, một cơ chế tuyệt vời để tái sử dụng code.
- Tạo module chia sẻ:
@Module({})
export class ShareModule {
static register(opts: { name: string }) {
return {
module: ShareModule,
providers: [
{
provide: 'SHAREMODULE',
useValue: () => xxx(opts)
}
]
}
}
}
- Sử dụng module chia sẻ:
@Module({
imports: [
ShareModule.register({ name: "Jack" })
]
})
export class AppModule {}
###.Controller
Controller xử lý các route HTTP. NestJS cho phép kiểm soát route một cách靈 hoạt.
Ví dụ:
import { Controller, Get, Post } from '@nestjs/common';
@Controller({ path: 'cat' })
export class AppController {
@Get('/all')
getAllCats() {
return [];
}
@Post()
createCat() {
return true;
}
}
###.Parameter & Body Handling
Xử lý các tham số từ URL và body:
@Get(':id')
getCatById(@Param('id') id: number) {}
@Post(':id')
getCatById(@Body("name") name: string) {}
###.Custom Decorators
Tạo các decorator tùy chỉnh:
export const Protocol = createParamDecorator(
(defaultValue: string, ctx: ExecutionContext) => {
const request = ctx.switchToHttp();
return request.protocol;
},
);
@Controller('mock')
export class MockController {
@Get()
async getList(@Protocol() protocol: string) {
console.log(protocol);
return protocol;
}
}
###.Scope
Các provider có thể có các lifecycle khác nhau: DEFAULT, TRANSIENT, REQUEST.
###.Service
Service xử lý nghiệp vụ chính:
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}
@Module({
providers: [AppService],
})
export class AppModule {}
###.Dependency Injection
NestJS sử dụng mạnh mẽ DI:
@Module({
controllers: [MockController],
providers: [Test],
})
export class MockModule {}
@Controller('mock')
export class MockController {
constructor(
private readonly testIns: Test,
) {}
}
###.Config
Sử dụng @nestjs/config để quản lý cấu hình:
@Module({
imports: [
ConfigModule.forRoot({
envFilePath: resolve(__dirname, '../../.env'),
}),
],
})
export class MockModule {}
###.Middleware
Tạo middleware:
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
async use(req: Request, res: Response, next: (error?: any) => void) {
const startTime = new Date();
res.on('finish', () => {
console.log(
`${req.method}: ${req.originalUrl}\t 时间: ${formatDate(startTime)}\t 耗时: ${(new Date().getTime() - startTime.getTime()) / 1000}s`
);
});
next();
}
}
@Module({
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes('*');
},
})
export class AppModule {}
###.Exception Filter
Xử lý các ngoại lệ:
@Catch()
export class HttpFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const code = exception.getStatus();
response.status(200).json({
code,
message: exception.message,
});
}
}
###.Pipe
Xử lý và validate dữ liệu:
@UsePipes(new ValidationPipe({}))
@Get()
async getOne(@Param('id', new ParseIntPipe()) id: number) {
return id;
}
###.Guard
Thực hiện kiểm soát truy cập:
@UseGuards(AuthenticationGuard)
@Get('user/:id')
async getUserInfo(@Param('id') userId: string) {
return {
name: 'Jack',
age: 10,
};
}
###.Interceptor
Xử lý và transform dữ liệu ra:
@UseInterceptors(BeautyResponseInterceptor)
@Get('users')
async getUsers() {
return [1, 2, 3];
}
###.Logger
Sử dụng logger để ghi log:
@Injectable()
export class AppService {
constructor(
private readonly logger: Logger,
) {}
@Get()
getHello() {
this.logger.log('Getting hello');
return 'Hello World!';
}
}
###.Versioning
Thực hiện versioning API:
import { Controller, Version, Get } from '@nestjs/common';
@Controller('mock')
export class MockController {
@Version('1')
@Get('version')
getVersion1() {
return 'version1';
}
@Version('2')
@Get('version')
getVersion2() {
return 'version2';
}
}
###.Task Scheduling
Thực hiện các task theo lịch:
@Cron('0 30 11 * * 1-5', {
timeZone: 'Asia/Shanghai',
})
handleCron() {
console.log('cron called...');
}
###.Tóm Lược
NestJS là một framework mạnh mẽ cho phép chúng ta phát triển ứng dụng Node.js một cách có tổ chức và dễ bảo trì. NestJS không chỉ cung cấp các công cụ và thư viện cần thiết, mà còn mang lại tư duy thiết kế tốt để phát triển ứng dụng scalable và maintainable.