Tìm hiểu và xây dựng cấu trúc api với NestJS (Phần 1)
Bài đăng này đã không được cập nhật trong 3 năm
Mở đầu
Ở bài trước mình có đi viết chương trình đầu tiên "hello word" sử dụng Nestjs. Ở bài này mình và các bạn sẽ đi tìm hiểu sâu hơn chút nữa và xây dựng cấu trúc api cơ bản nha.
Nội dung
1. Cài đặt Nestjs
Chúng ta sẽ dùng Nestjs cli để thuận tiện khi phát triển.
yarn add @nestjs/cli -g
Sau đó chúng ta sử dụng command nest -v
để kiểm tra version.
2. Khởi tạo project
chúng ta sử dụng command để khởi tạo project
nest new nestjs-task-management
Sau khi sử dụng lệnh project sẽ được khởi tạo với cấu trúc cơ bản (chúng ta đã đi qua cấu trúc này ở bài trước)
3. Tìm hiểu và xây dựng cấu trúc
3.1 Tìm hiểu cấu trúc module.
Một số công dụng của module.
- Mỗi một ứng dụng có it nhất một module "root module". Đó là điểm khởi đầu của ứng dụng.
- Module là một cách hiệu quả để tổ chức các chức năng liên quan đến nhau.
- Nó là nơi có thể quản lý tập trung các component của chức năng phát triển.
- Module là
singletons
do vậy một module có thể được import bởi các module khác.
Một module được xác định bởi hàm @module
với cấu trúc.
providers
là mảng các providers đã được định nghĩa trong module thông qua injection
.
controllers
là mảng các controllers đã được khởi tạo trong module.
exports
là mảng các providers để export cho các modules khác.
imports
là danh sách các module import vào module này. Giờ đây chúng ta có thể sử dụng tất cả các exported provider thông qua denpendency injection.
(tìm hiểu thêm denpendency injection ở phần dưới)
Trên đây là một ví dụ về cấu trúc module.
3.2 Thực thành khởi tạo module.
Chúng ta sử dụng command nest g module task
để tạo module.
Sau khi chạy lệnh một file module sẽ được tạo và injection trong app.module
.
import { Module } from '@nestjs/common';
import { TaskModule } from './task/task.module';
@Module({
imports: [TaskModule],
})
export class AppModule {}
Bên trong file task.module.ts
import { Module } from '@nestjs/common';
@Module({})
export class TaskModule {}
3.3 Tìm hiểu cấu trúc controller
Một số công dụng của controller
- Chịu trách nhiệm xử lý và phản hồi các yêu cầu đến từ client.
- Ràng buộc cụ thể của đường dẫn (ví dụ: /tasks hoặc task/:id)
- Chứa endpoints và request methods (GET, POST, DELETE ...)
- Có thể
dependency injection
cácproviders
khác trong cùng một module.
trên đây là chu trình xử lý của controller.
3.4 Thực thành khởi tạo controller
sử dụng nest g controller {name controller}--spec-no
để tạo controller và không tạo file test.
Sử dụng nest g controller {name controller}
để tạo controller cùng file test.
Chúng ta thấy file controller được khởi tạo và được injection trong task.module
.
File task.module.ts
import { Module } from '@nestjs/common';
import { TaskController } from './task.controller';
@Module({
controllers: [TaskController],
})
export class TaskModule {}
File task.controller.ts
import { Controller } from '@nestjs/common';
@Controller('task')
export class TaskController {}
3.5 Tìm hiểu Providers trong nestjs
- Có thể injected vào
contructors
sau khi sử dụng `@injectable - Có thể là một giá trị đơn giản, một class, sync/async factory..
- có thể
export
một module và sẵn sàng cho module khác import
3.6 Tìm hiểu service trong nestjs
- Định nghĩa service là một providers. Nhưng không phải tất cả providers là servicers.
- Công dụng của service là xử lý logic. Ví dụ: một service sẽ được gọi từ một controller để validate dữ liệu, tạo mới bản ghi vào
database
và trả về kết quả.
3.7 Tìm hiểu Dependency Injection trong nestjs
- Bất kì thành phần nào của hệ sinh thái NestJs cũng có thể
inject
một provider sau cú pháp@Injectable
- Chúng ta sẽ định nghĩa
dependencies
trong contructor của class. Nó sẽ sẵn sàng để chúng ta sử dụng trong class.
3.8 Thực hành tạo service và chạy thử luồng.
Chúng ta sử dụng command nest g service tasks --no-spec
tương tự như controller --no-spec
để không tạo cùng file test.
Sau khi sử dụng lệnh trên một file service sẽ được tạo ra đồng thời đã được Injected vào task module.
Bây giờ chúng ta thực hành viết một api đơn giản sử dụng kết hợp controller, service
File task.controller.ts
import { Controller, Get } from '@nestjs/common';
import { TaskService } from './task.service';
@Controller('task')
export class TaskController {
constructor(private taskService: TaskService) {}
@Get('/get-all')
getAllTask() {
return this.taskService.getAllTask();
}
}
File task.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class TaskService {
getAllTask() {
return '1213';
}
}
Và kết quả khi chúng ta chạy trên postman
Như vậy chúng ta đã dựng thành công luồng.
3.9 Tìm hiểu và xây dựng dto
Data transfer object
là các class định nghĩa số lượng, kiểu dữ liệu của đối tượng qua đó giảm được số lượng dữ liệu không cần thiết khi nhận hoặc truyền đi và cũng tăng cường độ bảo mật. Có thể hiểu cách khác là đóng gói data để chuyển giữa client - server hoặc giữa các service trong microservice.
Một số lưu ý quan trọng với DTO
DTO không bắt buộc phải có khi phát triển. Vì chúng chỉ có công dụng kiểm soát được dữ liệu đầu vào. Tuy nhiên, giá trị nó mang lại đáng để chúng ta sử dụng.
Chúng ta nên thêm vào ứng dụng càng sớm, càng tốt để dễ bảo trì cũng như refactor code.
Chúng ta tạo thư mục DTO và thêm file create-task.dto.ts
.
export class CreateTaskDto {
title: string;
description: string;
}
File controller
import { Body, Controller, Get, Post } from '@nestjs/common';
import { CreateTaskDto } from './dto/create-task.dto';
import { TaskService } from './task.service';
@Controller('task')
export class TaskController {
constructor(private taskService: TaskService) {}
@Get('/get-all')
getAllTask() {
return this.taskService.getAllTask();
}
@Post()
createTask(@Body() createTaskDto: CreateTaskDto) {
return this.taskService.createTaskDto(createTaskDto);
}
}
File service
import { Injectable } from '@nestjs/common';
import { CreateTaskDto } from './dto/create-task.dto';
@Injectable()
export class TaskService {
getAllTask() {
return '1213';
}
createTaskDto(createTaskDto: CreateTaskDto) {
const { title, description } = createTaskDto;
return { title, description };
}
}
4. Tổng kết lại kiến thức trong bài
Chúng ta đang hướng tới tạo một cấu trúc
repuest -> controller -> service(xử lý logic, quản lý giá trị đầu vào) -> database
.
Chúng phân tách, kiểm soát ngay từ đầu như vậy sẽ dễ dàng cho chúng ta matain, refator hệ thống trong tương lai.
Kết luận
Qua bài này chúng ta đã hiểu thêm về luồng chúng ta có thể xây dựng, các lý thuyết cơ bản của NestJS. Ở bài sau chúng ta tiếp tục tìm hiểu về cách thao tác với Database.
Tài liệu tham khảo
All rights reserved