-5

Tìm hiểu và xây dựng cấu trúc api với NestJS (Phần 1)

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ác providers 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

Khóa học https://www.udemy.com/course/nestjs-zero-to-hero


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí