• Privacy Policy
Chia sẻ kiến thức công nghệ
  • Home
  • Lập trình
  • Chia sẻ
  • Blog
No Result
View All Result
  • Home
  • Lập trình
  • Chia sẻ
  • Blog
No Result
View All Result
Duy PT Blog
No Result
View All Result
Home NodeJS

NestJS – Providers

Duy Solo by Duy Solo
August 29, 2021
in NodeJS
Reading Time: 8 mins read
A A
Share on FacebookShare on Twitter

Mục lục

  1. Providers
  2. Dependency Injection
  3. Providers cơ bản
    1. useClass
    2. useValue
    3. useFactory
    4. useExisting
  4. Non-class provider tokens
  5. Export Provider
  6. Kết

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:

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

@Injectable()
export class AppService {
  getHello(): string {
    return "Hello World!";
  }
}
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:

@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().

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é.

Related Posts

Lập trình

Xóa một property của Object trong Javascript

by Duy Solo
April 24, 2022
0

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, một cách...

Read more
Higher order Functions là gì? Tại sao nên sử dụng chúng?
Lập trình

Higher order Functions là gì? Tại sao nên sử dụng chúng?

by Duy Solo
April 15, 2022
0

Đâ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...

Read more
NodeJS

Các design pattern cần biết khi làm việc với NestJS – phần 2

by Duy Solo
August 28, 2021
0

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...

Read more
NodeJS

Các design pattern cần biết khi làm việc với NestJS

by Duy Solo
August 28, 2021
0

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...

Read more
Load More
Next Post
Cài đặt LEMP stacks trên CentOS 8

Cài đặt LEMP stacks trên CentOS 8

Higher order Functions là gì? Tại sao nên sử dụng chúng?

Higher order Functions là gì? Tại sao nên sử dụng chúng?

Discussion about this post

POPULAR POST

  • LocalStorage và Cookies – chọn cái nào để lưu JWT Tokens hiệu quả và an toàn?

    1066 shares
    Share 426 Tweet 267
  • Hướng dẫn reset trial cho các sản phẩm IntelliJ trên MacOS

    933 shares
    Share 373 Tweet 233
  • Repository Design Pattern và ứng dụng của nó trong Laravel

    919 shares
    Share 368 Tweet 230
  • Decorators trong Typescript

    848 shares
    Share 339 Tweet 212
  • Các thư viện Date Pickers tốt nhất cho React

    743 shares
    Share 297 Tweet 186
Duy PT Blog

© 2021 Duy PT Blog

Liên kết

  • Lập trình
  • Chia sẻ
  • Blog
  • Privacy Policy

Theo dõi

No Result
View All Result
  • Home

© 2021 Duy PT Blog