NestJS
1.Giới thiệu
NestJS là một framework Node.JS cho phép xây dựng ứng dụng phía server. Nest mở rộng các framework Node.js như Express hay Fastify để bổ sung thêm nhiều module hay thư viện hỗ trợ việc xử lý tác vụ. Đây là một framework mã nguồn mở, sử dụng TypeScript và rất linh hoạt để xây dựng các hệ thống backend.
2.Tạo một dự án
Môi trường cần chuẩn bị
Node | Yarn | Nestjs | MySQL |
---|---|---|---|
v16.16.0 | 1.22.19 | 9.0.0 | 8.0.26 |
Cài đặt Nest CLI để tạo một project bằng hai lệnh dưới đây:
yarn global add @nestjs/cli
nest new todos
Sau khi chạy 2 lệnh trên ta sẽ có một source code với cấu trúc như sau:
Ta chỉ cần để ý đến thư mục src với ba file được tạo sẵn:
- main.ts: File để khởi tạo các đối tượng chạy ứng dụng, chẳng hạn ta có thể dùng lệnh NestFactory.create() để tạo instance cho Nest.
- app.module.ts: Là module gốc của ứng dụng, có trách nhiệm đóng gói mọi thứ có trong project.
- app.controller.ts: Chứa các router để xử lý các request và trả về response cho client.
- app.services.ts: Chứa các hàm xử lý logic cho service, chẳng hạn như ứng dụng có service kết nối đến DB hoặc xử lý file,…
- app.controller.spec.ts: File dùng để viết unit test cho các controller.
Nhìn chung thì có ba thành phần chính trong NestJS: controller, provider và module.
main.ts sẽ sử dụng static method create() của NestFactory để tạo server app như sau:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
kiểm tra hoạt động ban đầu
cd todos
yarn start:dev
Truy cập: http://localhost:3000
3.Cài đặt TypeORM và DB
cd todos
yarn add @nestjs/typeorm typeorm mysql class-transformer class-validator
yarn add @nestjs/typeorm typeorm mysql2 reflect-metadata
yarn add -D typeorm-extension
Tạo ba File cấu hình.
src / config / ormconfig.ts
import { DataSourceOptions } from 'typeorm';
const ormconfig: DataSourceOptions = {
name: 'default',
type: 'mysql',
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT, 10),
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE,
synchronize: false,
logging: false,
connectTimeout: 30 * 1000,
entities: [process.cwd() + '/dist/**/entities/**/*.entity.js'],
migrations: [process.cwd() + '/dist/database/migrations/**/*.js'],
charset: 'utf8mb4_general_ci',
};
export default ormconfig;
src / config / ormdatasource.ts
import { DataSource } from 'typeorm';
import ormconfig from './ormconfig';
export const AppDataSource = new DataSource(ormconfig);
.env
# DB
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USERNAME=root
DB_PASSWORD=1234
DB_DATABASE=todolist
4.Table định nghĩa
Column_Name | Type |
---|---|
task_id | integer |
title | varchar |
due_date | date |
status | tinyint |
created_at | datetime |
updated_at | datetime |
-- todolist.task definition
CREATE TABLE `task` (
`task_id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(20) NOT NULL,
`due_date` date NOT NULL,
`status` tinyint(4) DEFAULT '1',
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`task_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
5.Tạo entity
cd todos
mkdir src/entities
touch $_/task.entity.ts
update code vào task.entity.ts
import {
Entity,
Column,
PrimaryGeneratedColumn,
CreateDateColumn,
UpdateDateColumn,
} from 'typeorm';
@Entity()
export class Task {
@PrimaryGeneratedColumn()
readonly task_id: number;
@Column('varchar', { length: 20, nullable: false })
title: string;
@Column('date', { nullable: false })
due_date: Date;
@Column('tinyint', { width: 1, default: 1 })
status: number;
@CreateDateColumn()
readonly created_at?: Date;
@UpdateDateColumn()
readonly updated_at?: Date;
}
6.Tạo Controller
Khi có request HTTP đến, cơ chế routing sẽ chuyển request này đến controller tương ứng để xử lý và trả về phản hồi thích hợp. Để tạo một controller thì ta dùng @Controller() để liên kết class Controller với request tương ứng.
Sử dụng câu lệnh để Nest CLI tạo tự động:
yarn nest g controller task
src/task/task.controller.ts
Tệp được tạo có nội dung:
import { Controller } from '@nestjs/common';
@Controller('task')
export class TaskController{}
Tất nhiên, đây chỉ là một controller rỗng, chưa có làm gì cả.
Sau khi implement logic create, read, update, delete vào. Thực hiện gọi TaskService
import { Controller, Get, Post, Put, Delete, Body, Param } from '@nestjs/common';
import { TaskService } from './task.service';
import { Task } from '../entities/task.entity';
import { CreateTaskDTO, UpdateTaskDTO } from './task.dto';
import { InsertResult, UpdateResult, DeleteResult } from 'typeorm';
@Controller('task')
export class TaskController {
constructor(private readonly service: TaskService) {}
@Get()
async getTaskList(): Promise<Task[]> {
return await this.service.findAll();
}
@Post()
async addTask(@Body() task: CreateTaskDTO): Promise<InsertResult> {
return await this.service.create(task);
}
@Get(':id')
async getTask(@Param('id') id: string): Promise<Task> {
return await this.service.find(Number(id));
}
@Put(':id/update')
async update(
@Param('id') id: string,
@Body() task: UpdateTaskDTO,
): Promise<UpdateResult> {
return await this.service.update(Number(id), task);
}
@Delete(':id')
async deleteTask(@Param('id') id: string): Promise<DeleteResult> {
return await this.service.delete(Number(id));
}
}
7.Tạo Service
yarn nest g service task
src/task/task.service.ts
Tệp được tạo.
Sau khi implement logic create, read, update, delete vào.
import { Injectable } from '@nestjs/common';
import { Task } from 'src/entities/task.entity';
import { Repository, InsertResult, UpdateResult, DeleteResult } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { CreateTaskDTO } from './task.dto';
@Injectable()
export class TaskService {
constructor(
@InjectRepository(Task)
private readonly taskRepository: Repository<Task>
) {}
async findAll(): Promise<Task[]> {
return await this.taskRepository.find();
}
async create(Task: CreateTaskDTO): Promise<InsertResult> {
return await this.taskRepository.insert(Task);
}
async find(id: number): Promise<Task> | null {
return await this.taskRepository.findOne({where:{ task_id: id }});
}
async update(id: number, Task): Promise<UpdateResult> {
return await this.taskRepository.update(id, Task);
}
async delete(id: number): Promise<DeleteResult> {
return await this.taskRepository.delete(id);
}
}
8.Tạo Module
Một module được thiết kế để đóng gói các logic liên quan của những chức năng cần triển khai đến client một cách độc lập. Một module trong Nest là class được định nghĩa với decorator @Module() của Nest, dùng để mô tả các thuộc tính như controller, provider hay dependency của module này.
Tạo một Mô-đun bằng lệnh sau.
yarn nest g module task
src/task/task.module.ts
Sẽ được tạo, sau chỉnh sửa
import { Module } from '@nestjs/common';
import { TaskController } from './task.controller';
import { TaskService } from './task.service';
import { Task } from 'src/entities/task.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
controllers: [TaskController],
imports: [TypeOrmModule.forFeature([Task])],
providers: [TaskService],
})
export class TaskModule {}
9.Tạo DTO
touch src/task/task.dto.ts
src / task / task.dto.ts
import { IsNotEmpty, IsString, IsOptional } from 'class-validator';
export class CreateTaskDTO {
@IsNotEmpty()
@IsString()
title: string;
@IsNotEmpty()
@IsString()
due_date: string;
}
export class UpdateTaskDTO {
@IsOptional()
@IsNotEmpty()
@IsString()
title: string;
@IsOptional()
@IsNotEmpty()
@IsString()
due_date: string;
}
Sau đó, update code main.ts để kích hoạt validator trong DTO.
src / main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
@IsNotEmpty()@IsString()
dùng để validator dữ liệu.
Giải thích chi tiết tham khảo :
https://www.npmjs.com/package/class-validator
10.Kiểm tra hoạt động
yarn start:dev
Truy cập: http://localhost:3000/task
Màn hình trên trình duyệt
[]
Sau đó, chèn dữ liệu mới bằng curl
curl http://localhost:3000/task -X POST -d "title=nestjs&due_date=2022-08-25"
Kết quả thực thi
{"identifiers":[{"task_id":1}],"generatedMaps":[{"task_id":1,"status":1,"created_at":"2022-08-26T13:04:14.550Z","updated_at":"2022-08-26T13:04:14.550Z"}],"raw":{"fieldCount":0,"affectedRows":1,"insertId":1,"serverStatus":2,"warningCount":0,"message":"","protocol41":true,"changedRows":0}}
Truy cập: http://localhost:3000/task/1
Sau đó, chỉ dữ liệu với id 1 được hiển thị.
[{"task_id":1,"title":"nestjs","due_date":"2022-08-25","status":1,"created_at":"2022-08-26T13:07:29.018Z","updated_at":"2022-08-26T13:07:29.018Z"}]
Tương tự kiểm tra Update, Delete
curl http://localhost:3000/task/1/update -X PUT -d "title=nestjs update"
curl http://localhost:3000/task/1 -X DELETE
Full Source code : https://github.com/lenhht/todos
11.Kết luận
Như vậy chúng ta đã biết được cách dùng framework nestjs cơ bản.
Bài tiếp theo sẽ tìm hiểu về middleware, cache, microservice, run batch trong nestjs