0
0
Lập trình
Admin Team
Admin Teamtechmely

Module Động trong NestJS: 3 Khái Niệm Quan Trọng Cần Hiểu - Register, forRoot, forFeature

Đăng vào 1 tháng trước

• 5 phút đọc

Trong loạt bài viết về NestJS, một trong những khái niệm được giới thiệu và thường gặp trong các buổi phỏng vấn là Module Động (Dynamic Module). Vậy Module Động là gì và tại sao nó lại quan trọng đối với lập trình viên NestJS trong phát triển Backend?

Dynamic Module trong NestJS là một tính năng mạnh mẽ cho phép bạn xây dựng các module có thể cấu hình linh hoạt. Tính năng này rất hữu ích khi bạn cần tái sử dụng một module với các cấu hình khác nhau hoặc khi có nhiều điều kiện cụ thể cần đáp ứng trong ứng dụng của bạn.

Bước 1: Tạo Module Động

Cấu trúc Module

typescript Copy
// database.module.ts
import { Module, DynamicModule } from '@nestjs/common';

@Module({})
export class DatabaseModule {
  static forRoot(options: { type: string; host: string }): DynamicModule {
    const providers = [
      {
        provide: 'DATABASE_CONNECTION',
        useFactory: async () => {
          if (options.type === 'mysql') {
            return `Kết nối MySQL tại ${options.host}`;
          } else if (options.type === 'mongodb') {
            return `Kết nối MongoDB tại ${options.host}`;
          }
          throw new Error('Loại cơ sở dữ liệu không được hỗ trợ!');
        },
      },
    ];

    return {
      module: DatabaseModule,
      providers: providers,
      exports: providers,
    };
  }
}

Bước 2: Sử dụng Module Động trong ứng dụng

typescript Copy
// app.module.ts
import { Module } from '@nestjs/common';
import { DatabaseModule } from './database.module';

@Module({
  imports: [
    DatabaseModule.forRoot({
      type: 'mysql',
      host: 'localhost',
    }),
  ],
})
export class AppModule {}

Bước 3: Inject và sử dụng kết nối

typescript Copy
// app.service.ts
import { Injectable, Inject } from '@nestjs/common';

@Injectable()
export class AppService {
  constructor(@Inject('DATABASE_CONNECTION') private readonly connection: string) {}

  getConnectionInfo(): string {
    return this.connection;
  }
}

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

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

  @Get('connection')
  getConnection(): string {
    return this.appService.getConnectionInfo();
  }
}

Giải thích về Module Động

  1. DatabaseModule:

    • Đây là một module động.
    • Phương thức forRoot trả về một Dynamic Module với khả năng cấu hình linh hoạt.
    • Dựa trên options.type, nó sẽ tạo một kết nối tương ứng cho MySQL hoặc MongoDB.
  2. Sử dụng trong AppModule:

    • Gọi DatabaseModule.forRoot(...) để cung cấp cấu hình kết nối cơ sở dữ liệu.
  3. Inject và sử dụng trong service/controller:

    • Sử dụng DATABASE_CONNECTION để lấy thông tin kết nối cơ sở dữ liệu.

Chạy ứng dụng

Khi bạn chạy ứng dụng và truy cập endpoint /connection, bạn sẽ nhận được kết quả như sau (giả sử typemysqlhostlocalhost):

Copy
"Kết nối MySQL tại localhost"

Triển khai DatabaseModule với forRoot()forFeature()

1. Cấu hình DatabaseModule

typescript Copy
// database.module.ts
import { Module, DynamicModule } from '@nestjs/common';

@Module({})
export class DatabaseModule {
  static forRoot(options: { type: string; host: string }): DynamicModule {
    const providers = [
      {
        provide: 'DATABASE_CONNECTION',
        useValue: `Kết nối ${options.type} tại ${options.host}`,
      },
    ];

    return {
      module: DatabaseModule,
      providers: providers,
      exports: providers,
    };
  }

  static forFeature(entities: string[]): DynamicModule {
    const providers = entities.map((entity) => ({
      provide: `${entity.toUpperCase()}_REPOSITORY`,
      useValue: `${entity} repository`,
    }));

    return {
      module: DatabaseModule,
      providers: providers,
      exports: providers,
    };
  }
}
  • forRoot(options): Cung cấp thông tin kết nối chung cho cơ sở dữ liệu.
  • forFeature(entities): Định nghĩa repository cụ thể cho các bảng trong cơ sở dữ liệu.

2. Sử dụng forRoot() trong AppModule

typescript Copy
// app.module.ts
import { Module } from '@nestjs/common';
import { DatabaseModule } from './database.module';
import { UserModule } from './user.module';

@Module({
  imports: [
    DatabaseModule.forRoot({ type: 'mysql', host: 'localhost' }),
    UserModule,
  ],
})
export class AppModule {}

3. Sử dụng forFeature() trong module con

Tạo UserModule

typescript Copy
// user.module.ts
import { Module } from '@nestjs/common';
import { DatabaseModule } from './database.module';
import { UserService } from './user.service';

@Module({
  imports: [
    DatabaseModule.forFeature(['user', 'profile']),
  ],
  providers: [UserService],
})
export class UserModule {}

Dịch vụ sử dụng repository

typescript Copy
// user.service.ts
import { Injectable, Inject } from '@nestjs/common';

@Injectable()
export class UserService {
  constructor(  
    @Inject('USER_REPOSITORY') private readonly userRepository: string,
    @Inject('PROFILE_REPOSITORY') private readonly profileRepository: string,
  ) {}

  getRepositories(): string[] {
    return [this.userRepository, this.profileRepository];
  }
}

// user.controller.ts
import { Controller, Get } from '@nestjs/common';
import { UserService } from './user.service';

@Controller('users')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Get('repositories')
  getRepositories(): string[] {
    return this.userService.getRepositories();
  }
}

Kết quả

Khi bạn truy cập vào endpoint /users/repositories, bạn sẽ nhận được:

Copy
[ "user repository", "profile repository" ]

Chương trình sẽ sử dụng kết nối MySQL được cấu hình bởi forRoot() và module UserModule nhận thêm các repository thông qua forFeature().

Khi nào nên dùng forRoot()forFeature()?

Sử dụng forRoot() khi:

  • Cần cấu hình chung cho toàn ứng dụng.
  • Chỉ cần gọi một lần trong module gốc (ví dụ: AppModule).

Sử dụng forFeature() khi:

  • Cần mở rộng cấu hình cho các module con.
  • Định nghĩa các entities hay repository cho các bảng dữ liệu.

Tóm tắt

  • forRoot() = Cấu hình toàn ứng dụng.
  • forFeature() = Cấu hình cục bộ cho từng module con.
  • Kết hợp forRoot()forFeature() sẽ tạo ra hệ thống module linh hoạt và mô-đun, đặc biệt trong các dự án lớn.

Tài liệu tham khảo

Hãy đọc bài viết gốc tại: Trần Nhật Sang để tìm hiểu sâu hơn về Dynamic Module trong NestJS.
source: viblo

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào