NestJS - Providers

Providers là thành phần cơ bản và cực kỳ quan trọng trong Nest để thực hiện Dependency Injection.
NestJS - Providers
NestJS - Providers

Providers

Trong bài viết trước, mình có nhắc đến khái niệm Dependency Injection (DI), đi kèm đó là 3 khái niệm class

  • Client
  • Service
  • Injector

Trong Nest, providers đại diện cho các class Service. Điều đó có nghĩa là các providers sẽ được inject vào những nơi cần sử dụng thông qua DI. Một số class cơ bản trong Nest được xem là provider như

  • Service
  • Factory
  • Repository
  • Helper
  • ...

 

Dependency Injection

Nhờ Typescript, chúng ta có thể dễ dàng quản lý các dependencies trong Nest thông qua kiến trúc type-mapping. 

Bạn có thể tham khảo ví dụ sau:

//app.service.ts

import { Injectable } from "@nestjs/common";

@Injectable()
export class AppService {
  getHello(): string {
    return "Hello World!";
  }
}
//app.controller.ts

import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}

Trong AppService, chúng ta định nghĩa nó là một dependency bằng cách sử dụng decorator @Injectable().

Ngoài ra, AppService được inject vào AppController thông qua constructor.

constructor(private readonly appService: AppService) {}

Việc sử dụng Access Modifier (public - protected - private) trong constructor là một cách viết tắt giúp chúng ta có thể khai báo và khởi tạo property trong class cùng một lúc.

Đoạn code trên tương đương với cách viết như sau:

//app.controller.ts

@Controller()
export class AppController {
  private readonly appService: AppService
  
  constructor(appService: AppService) {
    this.appService = appService
  }

  ...
}

Một điều quan trọng nữa là các bạn cần register AppService trong Nest module để nó resolve và thực hiện inject AppService đúng cách.

Quá trình cũng rất đơn giản. Chúng ta chỉ cần chỉnh sửa file Nest module, sau đó thêm AppService vào mảng providers của decorator @Module().

//app.module.ts

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

 

Nest cung cấp sẵn một hệ thống IoC Container riêng giúp thực hiện DI một cách dễ dàng. Mình sẽ tóm tắt cơ chế hoạt động của nó như sau

  • Trong app.service.ts, decorator @Injectable() khai báo cho IoC Container biết AppService là một dependency được quản lý bởi IoC Container.
  • Trong app.controller.ts, controller AppController khai báo nó cần dependency AppService thông qua constructor.
  • Trong app.module.ts, Nest tạo ra một token liên kết với AppService từ tập tin app.service.ts. Việc đăng ký được thực hiện thông qua mảng providers của decorator @Module().
  • Khi Nest khởi tạo AppController, nó sẽ tìm kiếm tất cả các dependencies mà AppController yêu cầu - ở đây là AppService.
  • IoC Container lúc này sẽ kiểm tra tất cả providers đã được register trong Nest module và tìm AppService thông qua AppService token đã được đăng ký trước đó.
  • Sau khi tìm thấy, nó sẽ khởi tạo (hoặc lấy về nếu đã tồn tại) instance của AppService, sau đó khởi tạo class AppController, đồng thời inject AppService vào AppController thông qua constructor.
  • Nếu không tìm thấy, Nest sẽ báo lỗi cho bạn biết. 
Nest can't resolve dependencies of the AppController (?). Please make sure that the argument AppService at index [0] is available in the AppModule context.

Ngoài ra, IoC Container còn thực hiện analysis các dependencies (Nest gọi nó là tạo Dependency Graph). Dependency Graph bảo đảm các dependencies được resolve theo thứ tự - về cơ bản là "từ dưới lên". 

Cơ chế này của IoC Container giúp chúng ta giải quyết vấn đề gặp phải ở bài viết trước - khi bạn có nhiều lớp Service phụ thuộc nhau.

 

Providers cơ bản

Đây là cách khai báo đơn giản nhất, có thể sử dụng trong hầu hết các trường hợp.

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})

Bạn có thể truyền trực tiếp các class name vào trong mảng providers. Cú pháp này là cách viết tắt của cách viết đầy đủ như sau:

providers: [
  {
    provide: AppService,
    useClass: AppService,
  }
]

Nest hỗ trợ một số phương thức để thực hiện resolve dependencies:

  • useClass
  • useValue
  • useFactory
  • useExisting

useClass

Bạn chỉ cần khai báo tên class bạn muốn sử dụng làm instance. Hãy nhớ, chỉ có các class đăng ký @Injectable() mới có thể sử dụng làm instance theo cách này.

 

useValue

Nest sẽ resolve dependency bằng chính instance hoặc value mà bạn khai báo. Cách này thường được dùng để viết mock test.

import { CatsService } from './cats.service';

const mockCatsService = {
  /* mock implementation
  ...
  */
};

@Module({
  imports: [CatsModule],
  providers: [
    {
      provide: CatsService,
      useValue: mockCatsService,
    },
  ],
})
export class AppModule {}

 

useFactory

Cú pháp này giúp chúng ta các provider một cách dynamic. Dependency sẽ được resolve bằng giá trị của hàm useFactory(). Bạn cũng có thể inject dependencies vào các factory phức tạp.

  • Bạn có thể truyền các optional params vào factory function
  • Nếu truyền params vào factory function, bạn cần khai báo thêm các dependencies cho factory thông qua property inject.
const connectionFactory = {
  provide: 'CONNECTION',
  useFactory: (optionsProvider: OptionsProvider) => {
    const options = optionsProvider.get();
    return new DatabaseConnection(options);
  },
  inject: [OptionsProvider],
};

@Module({
  providers: [connectionFactory],
})
export class AppModule {}

 

useExisting

Phương thức này giúp chúng ta alias các providers đã tồn tại.

@Injectable()
class LoggerService {
  /* implementation details */
}

const loggerAliasProvider = {
  provide: 'AliasedLoggerService',
  useExisting: LoggerService,
};

@Module({
  providers: [LoggerService, loggerAliasProvider],
})
export class AppModule {}

Ở ví dụ trên, string token `AliasedLoggerService` là một alias của LoggerService. Nếu scope của cả 2 provider này đều là SINGLETON, thì chúng sẽ trả về cùng một instance.

 

Non-class provider tokens

Như đã đề cập ở ví dụ trước đó, Nest cũng cho phép bạn register DI tokens bằng string hoặc Symbol

@Module({
  providers: [
    {
      provide: 'CAT_REPOSITORY',
      useValue: new CatRepository(),
    },
  ],
})
export class AppModule {}

Sau đó sử dụng decorator Inject() ở nơi cần sử dụng.

@Controller()
export class AppController {
  constructor(@Inject('MY_REPOSITORY') private readonly repository: CatRepository {}
  ...
}

 

Export Provider

Giả sử bạn có 2 module A và B. Nếu bạn muốn một provider khai báo trong module A có thể được dùng bởi module B, đây là lúc bạn cần export provider đó từ module A, sau đó import module A vào trong module B để sử dụng.

const connectionFactory = {
  provide: 'CONNECTION',
  useFactory: (optionsProvider: OptionsProvider) => {
    const options = optionsProvider.get();
    return new DatabaseConnection(options);
  },
  inject: [OptionsProvider],
};

@Module({
  providers: [connectionFactory],
  exports: ['CONNECTION'],
})
export class AppModule {}

Thực ra thì export luôn cái provider object cũng được, chả sao cả.

@Module({
  providers: [connectionFactory],
  exports: [connectionFactory],
})
export class AppModule {}

 

Kết

Mình xin phép kết thúc bài viết ở đây. Hẹn gặp lại trong phần tiếp theo nhé.

Comments

Bài viết nổi bật

Dạo gần đây đi đâu cũng nghe nói về microservices, người người nhà nhà rục rịch chuyển dịch hệ thống sang microservices. Trước khi đưa ra sự so sánh, mình sẽ khái quát một chút về Monolith Application và MicroServices một chút cho các bạn chưa biết nắm rõ hơn nhé.
PHP là ngôn ngữ được sử dụng rộng rãi nhất trên thế giới trong lập trình web. Nó cũng bị ghét nhất. Nhưng tại sao nhiều developer lại ghét nó đến vậy? Hôm nay chúng ta hãy cùng tìm hiểu lý do xem chúng có thuyết phục không nhé ^_^
Có khá nhiều bạn đã yêu cầu mình một bài viết về Repository Design Pattern. Vậy mục đích của nó là gì? Nó có thực sự cần thiết cho ứng dụng của bạn hay không? Những điểm mạnh, điểm yếu của nó là gì? Chúng ta cùng đi sâu tìm hiểu qua bài viết này nhé.
Lúc trước mình hay sử dụng cách này trên laptop phụ của mình, giờ mua license luôn rồi. Hôm nay mình xin chia sẻ cho bạn nào cần nhé.
Ở bài viết này mình sẽ hướng dẫn bạn bắt đầu xây dựng một ứng dụng HMVC với Laravel, và tận dụng sức mạnh của Composer khi quản lí modules.

Mục lục

Related posts

Javascript là một thành phần không thể thiếu đối với frontend developers. Tuy nhiên, ngay từ lúc ra đời, nó đã tồn tại khá nhiều vấn đề cần khắc phục. Đó là lý do tại sao từ 2015 (ES6) tới 2021 (ES12) ra đời nhằm giúp Javascript trở nên tốt hơn.
Dạo này mình làm việc với mấy bạn trên github, thấy hay xài mấy từ viết tắt mà mình không hiểu lắm. Thôi thì tổng hợp lại một list các từ viết tắt hay dùng trong github luôn cho ai cần :D
Dạo gần đây đi đâu cũng nghe nói về microservices, người người nhà nhà rục rịch chuyển dịch hệ thống sang microservices. Trước khi đưa ra sự so sánh, mình sẽ khái quát một chút về Monolith Application và MicroServices một chút cho các bạn chưa biết nắm rõ hơn nhé.
Cách bỏ qua câu lệnh --set-upstream quen thuộc cho các con lười
Mình sẽ giới thiệu 2 cách để xóa một property trong Javascript Object. Một cách sử dụng mutable - toán tử delete, một cách còn lại là immutable - tính năng Object Restructuring.
Đây là một khái niệm rất quan trọng trong Functional Programming. Ở đây mình sẽ cho ví dụ dựa trên Javascript, cụ thể là TypeScript, do đó mình hi vọng các bạn đã có một số kiến thức nhất định về JS trước. Điều này sẽ giúp bạn nắm bắt nội dung bài viết dễ dàng hơn.
Một số design patterns chính đang được sử dụng bởi NestJS mà bạn cần nắm rõ để làm việc với NestJS hiệu quả hơn.
Một số design patterns chính đang được sử dụng bởi NestJS mà bạn cần nắm rõ để làm việc với NestJS hiệu quả hơn.
NestJS - Controllers
1477
Trách nhiệm chính của controllers là xử lý các requests và phản hồi lại cho phía client.
Lúc trước mình hay sử dụng cách này trên laptop phụ của mình, giờ mua license luôn rồi. Hôm nay mình xin chia sẻ cho bạn nào cần nhé.
NestJS là một NodeJS framework tiến bộ để xây dựng các ứng dụng server-side hiệu quả, đáng tin cậy cùng với khả năng mở rộng dễ dàng.
Qua series này, các bạn sẽ hiểu hơn về sức mạnh của NodeJS cũng như xây dựng một kiến trúc ứng dụng xanh sạch đẹp hết sức có thể, thỏa mãn tinh thần Test - Driven Development.

Tin mới nhất

Javascript là một thành phần không thể thiếu đối với frontend developers. Tuy nhiên, ngay từ lúc ra đời, nó đã tồn tại khá nhiều vấn đề cần khắc phục. Đó là lý do tại sao từ 2015 (ES6) tới 2021 (ES12) ra đời nhằm giúp Javascript trở nên tốt hơn.
Dạo này mình làm việc với mấy bạn trên github, thấy hay xài mấy từ viết tắt mà mình không hiểu lắm. Thôi thì tổng hợp lại một list các từ viết tắt hay dùng trong github luôn cho ai cần :D
Dạo gần đây đi đâu cũng nghe nói về microservices, người người nhà nhà rục rịch chuyển dịch hệ thống sang microservices. Trước khi đưa ra sự so sánh, mình sẽ khái quát một chút về Monolith Application và MicroServices một chút cho các bạn chưa biết nắm rõ hơn nhé.
Cách bỏ qua câu lệnh --set-upstream quen thuộc cho các con lười
Bài viết này sẽ hướng dẫn các bạn cài đặt LEMP stack trên CentOS Stream 9 mới nhất trên Vultr Cloud VPS.
Mình sẽ giới thiệu 2 cách để xóa một property trong Javascript Object. Một cách sử dụng mutable - toán tử delete, một cách còn lại là immutable - tính năng Object Restructuring.
Đây là một khái niệm rất quan trọng trong Functional Programming. Ở đây mình sẽ cho ví dụ dựa trên Javascript, cụ thể là TypeScript, do đó mình hi vọng các bạn đã có một số kiến thức nhất định về JS trước. Điều này sẽ giúp bạn nắm bắt nội dung bài viết dễ dàng hơn.
Bài hướng dẫn này sẽ hướng dẫn các bạn cài đặt LEMP stack (Linux - Nginx - MySQL - PHP) trên CentOS 8.
NestJS - Providers
1211
Providers là thành phần cơ bản và cực kỳ quan trọng trong Nest để thực hiện Dependency Injection.
Một số design patterns chính đang được sử dụng bởi NestJS mà bạn cần nắm rõ để làm việc với NestJS hiệu quả hơn.
Một số design patterns chính đang được sử dụng bởi NestJS mà bạn cần nắm rõ để làm việc với NestJS hiệu quả hơn.
NestJS - Controllers
1477
Trách nhiệm chính của controllers là xử lý các requests và phản hồi lại cho phía client.
Javascript là một thành phần không thể thiếu đối với frontend developers. Tuy nhiên, ngay từ lúc ra đời, nó đã tồn tại khá nhiều vấn đề cần khắc phục. Đó là lý do tại sao từ 2015 (ES6) tới 2021 (ES12) ra đời nhằm giúp Javascript trở nên tốt hơn.
NestJS - Providers
1211
Providers là thành phần cơ bản và cực kỳ quan trọng trong Nest để thực hiện Dependency Injection.
Một số design patterns chính đang được sử dụng bởi NestJS mà bạn cần nắm rõ để làm việc với NestJS hiệu quả hơn.
Một số design patterns chính đang được sử dụng bởi NestJS mà bạn cần nắm rõ để làm việc với NestJS hiệu quả hơn.
NestJS - Controllers
1477
Trách nhiệm chính của controllers là xử lý các requests và phản hồi lại cho phía client.