Thứ năm, 12/12/2019 | 00:00 GMT+7

Bắt đầu với NestJS

Nếu bạn đã làm việc trên ứng dụng Node.js, bạn có thể nhận thấy rằng nó trở nên khó bảo trì hơn theo thời gian. Bạn càng thêm nhiều tính năng mới vào ứng dụng, thì cơ sở mã càng lớn.

Nest.js là một khung Node.js phía server để xây dựng các ứng dụng hiệu quả, tin cậy và có thể mở rộng. Nó cung cấp cho các ứng dụng backend một cấu trúc module để tổ chức mã thành các module riêng biệt. Nó được xây dựng để loại bỏ các cơ sở mã vô tổ chức.

Lấy cảm hứng từ Angular , Nest.js được xây dựng bằng TypeScript và sử dụng Express.js , điều này làm cho nó tương thích với phần lớn phần mềm trung gian Express.

Trong bài đăng này, bạn sẽ tạo một API RESTful nhỏ cho phép user tìm nạp, tạo và xóa sách trong hiệu sách.

Yêu cầu

Để hoàn thành hướng dẫn này, bạn cần :

Hiểu các khối xây dựng của Nest.js

Sau đây là các khối xây dựng được sử dụng khi xây dựng ứng dụng Nest.js:

  • Bộ điều khiển
  • Nhà cung cấp
  • Mô-đun

Ta sẽ bắt đầu bằng cách xem xét bộ điều khiển. Giống như hầu hết các khuôn khổ web, bộ điều khiển trong Nest.js chịu trách nhiệm xử lý mọi yêu cầu gửi đến và trả lại phản hồi cho phía client của ứng dụng. Ví dụ: nếu bạn thực hiện lệnh gọi API đến một điểm cuối cụ thể, giả sử /home , bộ điều khiển sẽ nhận được yêu cầu này và dựa trên các tài nguyên có sẵn, nó sẽ trả lại phản hồi thích hợp.

Nest.js được cấu trúc theo cách mà cơ chế định tuyến có thể kiểm soát bộ điều khiển nào sẽ chịu trách nhiệm xử lý một yêu cầu cụ thể.

Để xác định bộ điều khiển trong Nest.js, hãy tạo file @Controller() và bao gồm trình trang trí @Controller() như trong đoạn mã sau:

users.controller.ts
  import { Controller, Get } from '@nestjs/common';  @Controller('users') export class UsersController {  @Get()  findAll() {     return 'This will return all the users';  } } 

Tiền tố của users trong trình trang trí Bộ điều khiển sẽ nhắc UsersController xử lý bất kỳ /users GET yêu cầu nào trong một ứng dụng và trả lại phản hồi thích hợp như đã chỉ định. Yêu cầu HTTP khác được bộ điều khiển xử lý bao gồm POST , PUT , DELETE như ta sẽ thấy ở phần sau của hướng dẫn.

Sau khi tạo bộ điều khiển, nó cần được thêm vào định nghĩa module trước khi Nest.js có thể dễ dàng nhận ra nó. Đây có thể là ApplicationModule root hoặc bất kỳ module nào khác được tạo trong ứng dụng. Thông tin thêm về điều này trong phần module của bài đăng này.

Bây giờ ta hãy xem xét các nhà cung cấp .

Như đã đề cập trước đó, Nest.js lấy cảm hứng từ Angular và tương tự như một ứng dụng Angular, bạn có thể tạo một trình cung cấp và đưa nó vào bộ điều khiển hoặc các trình cung cấp khác. Các nhà cung cấp này còn gọi là dịch vụ và chúng được thiết kế để trừu tượng hóa bất kỳ hình thức phức tạp và logic nào.

Nhà cung cấp dịch vụ trong Nest.js là một lớp JavaScript với trình trang trí @Injectable() đặc biệt ở trên cùng. Ví dụ: bạn có thể tạo một dịch vụ để tìm nạp user :

users.service.ts
 import { Injectable } from '@nestjs/common'; import { User } from './interfaces/user.interface';  @Injectable() export class UsersService {   private readonly users: User[] = [];    create(user: User) {      this.users.push(user);   }    findAll(): User[] {     return this.users;   } } 

Trình cung cấp được tạo ở trên là một lớp có hai phương thức create()findAll() , được dùng để tạo và trả về tất cả user tương ứng. Và để dễ dàng giúp kiểm tra kiểu, một giao diện đã được sử dụng để chỉ định kiểu phần tử sẽ được các phương thức nhận.

Cuối cùng, hãy nhìn vào Mô-đun. Mô-đun cho phép bạn group các file liên quan. Chúng là các file @Module được trang trí bằng @Module trang trí @Module . Trình trang trí đính kèm này cung cấp metadata mà Nest sử dụng để tổ chức cấu trúc ứng dụng.

Mỗi ứng dụng Nest.js phải có ít nhất một module , thường được gọi là mô-đun root . Mô-đun root này là module cấp cao nhất và thường đủ cho một ứng dụng nhỏ. Nên chia một ứng dụng lớn thành nhiều module vì nó giúp duy trì cấu trúc của ứng dụng.

Nếu bạn có một ứng dụng quản lý nhiều dữ liệu hoặc chức năng về user , bạn có thể group bộ điều khiển, dịch vụ và các file liên quan khác thành một module duy nhất, như UsersModule :

import { Module } from '@nestjs/common'; import { UsersController } from './users.controller.ts'; import { UsersService } from './users.service.ts';  @Module({   controllers: [UsersController],   providers: [UsersService] })  export class UsersModule {} 

Trong ví dụ này, ta được xuất một UsersModule có chứa cả UsersControllerUsersService . Với điều này tại chỗ, sau đó ta có thể tiến hành nhập và sử dụng UsersModule trong module root của ứng dụng như trong đoạn mã sau:

... import { UsersModule } from './users/users.module';  @Module({   ... })  export class AppModule { } 

Có một số khái niệm quan trọng khác trong Nest.js:

  • DTO : Đối tượng truyền dữ liệu là đối tượng xác định cách dữ liệu sẽ được gửi qua mạng.
  • Giao diện : Giao diện TypeScript được sử dụng để kiểm tra kiểu và xác định các loại dữ liệu có thể được chuyển tới bộ điều khiển hoặc dịch vụ Nest.
  • Chèn phụ thuộc : Chèn phụ thuộc là một mẫu thiết kế được sử dụng để tăng hiệu quả và tính mô đun của các ứng dụng. Nó thường được sử dụng bởi các khuôn khổ lớn nhất để giữ cho mã sạch và dễ sử dụng hơn. Nest.js cũng sử dụng nó để tạo ra các thành phần kết hợp.

Với mẫu này, rất dễ dàng quản lý dependencies giữa các khối xây dựng như bộ điều khiển, trình cung cấp và module . Điều duy nhất cần thiết là xác định dependencies , ví dụ như UsersService() trong phương thức khởi tạo của bộ điều khiển như được hiển thị ở đây:

 ... @Controller('users') export class UsersController { constructor(private readonly usersService: UsersService){}  ... } 

Với một số khái niệm được trình bày ngắn gọn, bây giờ bạn có thể chuyển sang phần tiếp theo, nơi bạn sẽ sử dụng tất cả kiến thức thu được cho đến nay trong bài đăng này vì bạn sẽ học cách xây dựng một cách liền mạch API RESTful bằng Nest.js.

Như đã nêu trước đó trong bài đăng này, bạn sẽ tạo một ứng dụng mẫu giúp bạn nắm bắt tốt một số khái niệm cốt lõi của Nest.js.

Ứng dụng này sẽ dành riêng cho một hiệu sách. Ở cuối bài đăng, bạn sẽ tạo một dịch vụ vi mô cho phép user tạo và thêm một cuốn sách mới với một vài mô tả vào danh sách sách hiện có. Đây có thể là từ database , nhưng đảm bảo tính đơn giản trong bài đăng này, ta sẽ không thực sự kết nối ứng dụng của bạn với database . Nhưng thay vào đó, ta sẽ sử dụng dữ liệu giả về sách và khi một cuốn sách mới được tạo, ta sẽ đẩy và thêm nó vào danh sách.

Bước 1 - Cài đặt Nest.js

Để xây dựng ứng dụng Nest.js mới, bạn cần cài đặt global ứng dụng Nest CLI . Đây là một công cụ dòng lệnh được tạo riêng để tạo một ứng dụng Nest.js mới và cung cấp quyền truy cập vào một số lệnh để tạo các file khác nhau và tạo ra một ứng dụng có cấu trúc tốt.

Ngoài việc sử dụng công cụ CLI, bạn cũng có thể cài đặt một ứng dụng Nest.js mới bằng cách sao chép dự án khởi động từ GitHub bằng Git , nhưng với mục đích của hướng dẫn này, hãy chạy lệnh sau để cài đặt Nest CLI:

  • npm install -g @nestjs/cli

Điều này sẽ cung cấp cho bạn quyền truy cập vào lệnh nest để cài đặt dự án và các lệnh cụ thể khác của dự án.

Tiếp theo, chạy lệnh sau để cài đặt một dự án mới có tên bookstore-nest trong folder phát triển của bạn:

  • nest new bookstore-nest

Bạn sẽ được hỏi một số câu hỏi trong quá trình cài đặt, chỉ cần làm theo dấu nhắc và trả lời tương ứng. Tiếp theo, khi quá trình cài đặt hoàn tất, hãy chuyển folder làm việc của bạn sang dự án mới được tạo:

  • cd bookstore-nest

Khởi động ứng dụng bằng:

  • npm run start

Bạn cũng có thể chạy lệnh sau để sử dụng Nodemon cho dự án:

 // start the application using nodemon npm run start:dev 

Điều hướng đến http://localhost:3000 trong trình duyệt của bạn và bạn sẽ thấy Hello World ! thông báo như trong hình ảnh sau:

Khi dự án bắt đầu, hãy tạo module root .

Bước 2 - Tạo module

Hãy tạo một module cho hiệu sách. Để làm điều này, bạn sẽ tận dụng trình tạo file của Nest CLI. Chạy lệnh sau để tạo module mới cho ứng dụng:

  • nest generate module books

Thao tác này tạo một folder mới có tên books trong folder src . Trong folder books bạn sẽ tìm thấy file books.module.ts :

src / books / books / module.ts
 import { Module } from '@nestjs/common'; @Module({}) export class BooksModule {} 

Điều này được tạo ra bởi lệnh và module cũng đã được thêm vào app.module.ts , đây là module root của ứng dụng.

Tiếp theo, bạn sẽ tạo các tuyến đường cho các điểm cuối

Bước 3 - Tạo các tuyến và bộ điều khiển

Như đã đề cập trước đó, các tuyến tồn tại trong bộ điều khiển, vì vậy bạn cần tạo bộ điều khiển sẽ xử lý các điểm cuối riêng lẻ. , hãy sử dụng Nest CLI để tạo bộ điều khiển của bạn, chạy lệnh sau:

  • nest generate controller books

Điều này tạo ra một bộ điều khiển bên trong folder books .
Vì ta sẽ không kết nối với database bây giờ, hãy tạo một dữ liệu giả mẫu cho hiệu sách. Trong folder src , tạo một folder con có tên mocks và trong folder mới tạo, tạo một file books.mock.ts mới có tên books.mock.ts và thêm mã sau vào đó:

src / mocks / books.mock.ts
 export const BOOKS = [     { id: 1, title: 'First book', description: "This is the description for the first book", author: 'Olususi Oluyemi' },     { id: 2, title: 'Second book', description: "This is the description for the second book", author: 'John Barry' },     { id: 3, title: 'Third book', description: "This is the description for the third book", author: 'Clement Wilfred' },     { id: 4, title: 'Fourth book', description: "This is the description for the fourth book", author: 'Christian nwamba' },     { id: 5, title: 'Fifth book', description: "This is the description for the fifth book", author: 'Chris anderson' },     { id: 6, title: 'Sixth book', description: "This is the description for the sixth book", author: 'Olususi Oluyemi' }, ]; 

Tiếp theo, bạn sẽ tạo một dịch vụ để nắm giữ tất cả logic cho nhà sách.

Bước 4 - Cài đặt dịch vụ

Chạy lệnh sau để tạo một dịch vụ:

nest generate service books 

Lệnh này sẽ tạo một file mới có tên books.service.ts trong folder ./src/books .

Tiếp theo, mở file mới tạo và dán như sau:

/src/books/books.service.ts
    import { Injectable, HttpException } from '@nestjs/common';   import { BOOKS } from '../mocks/books.mock';    @Injectable()   export class BooksService {       books = BOOKS;        getBooks(): Promise<any> {           return new Promise(resolve => {               resolve(this.books);           });       }       getBook(bookID): Promise<any> {           let id = Number(bookID);           return new Promise(resolve => {               const book = this.books.find(book => book.id === id);               if (!book) {                   throw new HttpException('Book does not exist!', 404);               }               resolve(book);           });       }   } 

Đầu tiên, bạn đã nhập các module yêu cầu từ Nest.js và cả BOOKS từ dữ liệu giả mà bạn đã tạo trước đó.

Tiếp theo, bạn tạo hai phương thức khác nhau có tên getBooks()getBook() để truy xuất danh sách sách từ dữ liệu giả và chỉ tìm nạp một cuốn sách bằng cách sử dụng bookID làm tham số.

Tiếp theo, thêm phương thức sau vào /src/books/books.service.ts ngay sau phương thức getBook() :

src / books / books.service.ts
 import { Injectable, HttpException } from '@nestjs/common'; import { BOOKS } from '../mocks/books.mock'; @Injectable() export class BooksService {     books = BOOKS;     ...     addBook(book): Promise<any> {         return new Promise(resolve => {             this.books.push(book);             resolve(this.books);         });     } } 

Phương pháp trên sẽ được sử dụng để đẩy một cuốn sách mới vào danh sách hiện có

Cuối cùng, thêm phương thức cuối cùng để xóa một cuốn sách cụ thể bằng cách sử dụng bookID làm tham số:

src / books / books.service.ts
 import { Injectable, HttpException } from '@nestjs/common'; import { BOOKS } from '../mocks/books.mock'; @Injectable() export class BooksService {     books = BOOKS;     ...     deleteBook(bookID): Promise<any> {         let id = Number(bookID);         return new Promise(resolve => {             let index = this.books.findIndex(book => book.id === id);             if (index === -1) {                 throw new HttpException('Book does not exist!', 404);             }             this.books.splice(1, index);             resolve(this.books);         });     } } 

Bước 5 - Đưa Dịch vụ vào Bộ điều khiển

Ở đây, bạn sẽ sử dụng mẫu thiết kế chèn phụ thuộc để chuyển BooksService vào BooksController thông qua một hàm tạo. Mở BooksController đã tạo trước đó và dán mã sau vào đó:

src / books / books.controller.ts
 import { Controller, Get, Param, Post, Body, Query, Delete } from '@nestjs/common'; import { BooksService } from './books.service'; import { CreateBookDTO } from './dto/create-book.dto';  @Controller('books') export class BooksController {     constructor(private booksService: BooksService) { }      @Get()     async getBooks() {         const books = await this.booksService.getBooks();         return books;     }      @Get(':bookID')     async getBook(@Param('bookID') bookID) {         const book = await this.booksService.getBook(bookID);         return book;     }      @Post()     async addBook(@Body() createBookDTO: CreateBookDTO) {         const book = await this.booksService.addBook(createBookDTO);         return book;     }      @Delete()     async deleteBook(@Query() query) {         const books = await this.booksService.deleteBook(query.bookID);         return books;     } } 

Đầu tiên, các module quan trọng được nhập từ @nestjs/common và bạn cũng nhập cả BooksServiceCreateBookDTO tương ứng. CreateBookDTO là một đối tượng truyền dữ liệu, một lớp TypeScript được tạo để kiểm tra kiểu và xác định cấu trúc của một đối tượng trông như thế nào khi tạo một cuốn sách mới. Ta sẽ tạo DTO này sau một chút.

Tiếp theo, bạn đã sử dụng hàm constructor để đưa BooksService vào bộ điều khiển và tạo bốn phương thức khác nhau:

  • getBooks() : Được sử dụng để tìm nạp danh sách tất cả các sách. Nó có @Get() decorator được gắn vào nó. Điều này giúp ánh xạ mọi yêu cầu GET được gửi đến / sách tới bộ điều khiển này.
  • getBook() : Được sử dụng để truy xuất chi tiết của một cuốn sách cụ thể bằng cách chuyển bookID dưới dạng tham số.
  • addBook() : Dùng để tạo và đăng một cuốn sách mới vào danh sách sách hiện có. Và bởi vì ta không kiên trì vào database , cuốn sách mới được thêm vào sẽ chỉ được lưu giữ trong bộ nhớ.
  • deleteBook() : Được sử dụng để xóa sách bằng cách chuyển bookID làm tham số truy vấn.

Mỗi phương thức có một trình trang trí đặc biệt gắn liền với nó, điều này giúp dễ dàng định tuyến mỗi yêu cầu HTTP đến một phương thức cụ thể trong bộ điều khiển.

Bước 6 - Xác định DTO

Trong phần trước, bạn đã sử dụng một đối tượng truyền dữ liệu có tên là CreateBookDTO . Để tạo nó, hãy chuyển đến folder ./src/books và tạo một tên folder con mới dto . Tiếp theo, trong folder mới được tạo, tạo một file khác và gọi nó là create-book.dto.ts và dán nội dung sau vào đó:

src / books / dto / create-book.dto.ts
 export class CreateBookDTO {     readonly id: number;     readonly title: string;     readonly description: string;     readonly author: string; } 

Bạn gần như đã hoàn thành với ứng dụng. Điều hướng trở lại file ./src/books/books.module.ts bạn đã tạo trước đó và cập nhật nó bằng mã sau:

src / books / books.module.ts
 import { Module } from '@nestjs/common'; import { BooksController } from './books.controller'; import { BooksService } from './books.service'; @Module({   controllers: [BooksController],   providers: [BooksService] }) export class BooksModule {} 

Khởi động lại ứng dụng nếu hiện tại nó không chạy với:

  • npm run start

Sau đó, sử dụng người đưa thư để kiểm tra API

Tạo một số sách mới:

Nhận sách bằng ID:

Và xóa một cuốn sách:

Kết luận

Trong hướng dẫn này, bạn đã xem nhanh các nguyên tắc cơ bản và các khối xây dựng cơ bản của Nest.js và sau đó xây dựng một API RESTful.

Bạn sẽ tìm thấy mã nguồn hoàn chỉnh của hướng dẫn này tại đây trên GitHub .


Tags:

Các tin liên quan