Nest JS Part-2에서 동적 모듈 생성

32888 단어

Nest JS Part-2에서 동적 모듈 생성



동적 모듈에 대한 기본 개념을 얻으려면 Part-2로 이동하기 전에 이 블로그 시리즈의 Part-1을 확인하십시오. 여기 링크가 있습니다

코드 :
https://github.com/tkssharma/blogs/tree/master/nestjs-dynamic-module

이 블로그의 Part-1 일부를 마친 직후 시작합니다.
알겠습니다. Nestjs 동적 모듈로 외부 HTTP 클라이언트를 생성하는 사용 사례가 있습니다. 이 모듈은 axios 또는 httpClient와 동일한 API 호출을 수행할 수 있는 http 서비스 역할을 합니다.

이것은 데모용이며 이를 기반으로 모든 프로젝트의 어디에나 연결할 수 있는 다른 nestjs 동적 모듈을 만들 수 있습니다.
이와 같은 것을 갖는 우리의 최종 목표

동적 모듈에서 forRoot 및 forRootAsync와 같은 다양한 메서드를 모두 노출할 수 있어야 합니다.
forRootAsync는 동적 모듈을 반환해야 합니다.

    HttpClientModule.forRootAsync({
      imports: [AppConfigModule],
      inject: [AppConfigService],
      useFactory: (config: AppConfigService) => ({
        apiUrl: config.platformApi.baseUrl,
        apiKey: config.platformApi.apiKey,
      }),
    }),


시작하자
  • 이 모듈을 사용하여 외부 세계에 대한 http 호출을 처리할 수 있는 서비스 메서드를 노출하려고 합니다
  • .
  • 공급자와 함께 사용할 수 있는 일반 ES6 서비스가 필요합니다
  • .
  • 이러한 모든 메서드를 정적forRootAsyncforRoot로 사용하려면 HttpClient 모듈이 필요합니다.
  • 주입 가능한 공급자 및 필요한 토큰

  • 우리는 HttpClientModule 옵션을 얻고 그 메소드를 사용할 서비스를 작성할 것입니다.
    HttpClient 모듈의 경우 옵션은 URL 및 API 키 또는 API 호출에서 전달하려는 사용자 정의 헤더가 될 수 있습니다.

    export class HttpClientService {
      private readonly apiUrl: string = "";
      private readonly apiKey: string = "";
    
      constructor(
        @Inject(HTTP_CLIENT_MODULE_OPTIONS)
        private readonly options: HttpClientModuleOptions
      ) {
        this.apiUrl = this.options.apiUrl;
        this.apiKey = this.options.apiKey;
      }
    
      public async fetchData(method: string, payload?: any) {
        return axios({
          {
            method,
            url: `${this.apiUrl}/health`,
            data,
            headers: {
            "Content-Type": "application/json",
              Authorization: `Bearer ${this.apiKey}`,
            }
          }
        );
      }
    }
    



    export const HTTP_CLIENT_MODULE_OPTIONS = "HttpClientModuleOptions";
    export const HTTP_CLIENT_TOKEN = "HttpClientToken";
    export const HTTP_CLIENT_MODULE = "HttpClientModule";
    


    공급자를 사용HttpClientModuleOptions하고 반환할 수 있는 공급자를 만듭니다. 공급자는 주입 가능한 토큰 HTTP_CLIENT_TOKEN을 사용하고 해당 주입 가능한 토큰의 값은 HttpClientService 서비스의 인스턴스입니다.

    export function createHttpClientProvider(
      options: HttpClientModuleOptions
    ): Provider {
      return {
        provide: HTTP_CLIENT_TOKEN,
        useValue: getHttpClientModuleOptions(options),
      };
    }
    
    export const getHttpClientModuleOptions = (
      options: HttpClientModuleOptions
    ): HttpClientService => new HttpClientService(options);
    


    이제 공급자를 추가하기 위해 HttpClientModule에서 이createHttpClientProvider 함수를 사용할 수 있습니다.
    다음은 두 메서드 모두에 대한 정적 메서드인 Root 및 forRootAsync를 만드는 중요한 부분입니다.
    구조와 같은 모듈을 반환해야 합니다.

       {
          module: HttpClientModule,
          imports: options.imports,
          providers: [...this.createAsyncProviders(options), provider],
          exports: [provider],
        }
    



    @Global()
    @Module({})
    export class HttpClientModule {
      public static forRoot(options: HttpClientModuleOptions): DynamicModule {
        const provider: Provider = createHttpClientProvider(options);
        return {
          module: HttpClientModule,
          providers: [provider],
          exports: [provider],
        };
      }
    
      public static forRootAsync(
        options: HttpClientModuleAsyncOptions
      ): DynamicModule {
        const provider: Provider = {
          inject: [HTTP_CLIENT_MODULE_OPTIONS],
          provide: HTTP_CLIENT_TOKEN,
          useFactory: async (options: HttpClientModuleOptions) =>
            getHttpClientModuleOptions(options),
        };
    
        return {
          module: HttpClientModule,
          imports: options.imports,
          providers: [...this.createAsyncProviders(options), provider],
          exports: [provider],
        };
      }
    
      private static createAsyncProviders(
        options: HttpClientModuleAsyncOptions
      ): Provider[] {
        if (options.useExisting || options.useFactory) {
          return [this.createAsyncOptionsProvider(options)];
        }
    
        const useClass = options.useClass as Type<HttpClientModuleFactory>;
    
        return [
          this.createAsyncOptionsProvider(options),
          {
            provide: useClass,
            useClass,
          },
        ];
      }
    
      private static createAsyncOptionsProvider(
        options: HttpClientModuleAsyncOptions
      ): Provider {
        if (options.useFactory) {
          return {
            provide: HTTP_CLIENT_MODULE_OPTIONS,
            useFactory: options.useFactory,
            inject: options.inject || [],
          };
        }
    
        const inject = [
          (options.useClass ||
            options.useExisting) as Type<HttpClientModuleFactory>,
        ];
    
        return {
          provide: HTTP_CLIENT_MODULE_OPTIONS,
          useFactory: async (optionsFactory: HttpClientModuleFactory) =>
            await optionsFactory.createHttpModuleOptions(),
          inject,
        };
      }
    }
    


    forRoot 구현을 디코딩할 수 있습니다. 여기에서 우리는 DynamicModule을 반환하고 있으며 createHttpClientProvider에서 반환된 공급자를 사용하여 이를 내보내고 있습니다. createHttpClientProvider는 httpClientService의 인스턴스일 뿐입니다.

     public static forRoot(options: HttpClientModuleOptions): DynamicModule {
        const provider: Provider = createHttpClientProvider(options);
        return {
          module: HttpClientModule,
          providers: [provider],
          exports: [provider],
        };
      }
      // createHttpClientProvider will return this 
      {
        provide: HTTP_CLIENT_TOKEN,
        useValue: new HttpClientService(options)
      }
    


    비동기식 옵션 제공자의 다양한 형태

    비동기 공급자



    때때로 애플리케이션 시작은 하나 이상의 비동기 작업이 완료될 때까지 지연되어야 합니다. 예를 들어 데이터베이스와의 연결이 설정될 때까지 요청 수락을 시작하지 않으려는 경우가 있습니다. 비동기 공급자를 사용하여 이를 달성할 수 있습니다.
    https://docs.nestjs.com/fundamentals/custom-providers

    useClass



    @Module({
      imports: [
        HttpClientModule.forRootAsync({ useClass: ConfigService})
      ]
    })
    

    useFactory



    @Module({
      imports: [HttpClientModule.forRootAsync({
        useFactory: () => {
          return {
            host: "localhost",
            port: 5432,
            database: "nest",
            user: "john",
            password: "password"
          }
        }
      })]
    })
    

    사용기존



    @Module({
      imports: [HttpClientModule.registerAsync({
        useExisting: ConfigService
      })]
    })
    

    다중 비동기 옵션 공급자 기술 지원



    우리는 홈 스트레치에 있습니다. 이제 위에서 설명한 추가 기술을 지원하기 위해 forRootAsync() 메서드를 일반화하고 최적화하는 데 중점을 둘 것입니다. 완료되면 모듈은 세 가지 기술을 모두 지원합니다.
  • useClass - 옵션 공급자의 전용 인스턴스를 가져옵니다.
  • useFactory - 함수를 옵션 공급자로 사용합니다.
  • useExisting - 기존(공유, SINGLETON) 서비스를 옵션 공급자로 다시 사용합니다.
    이 모든 경우에 대한 코드를 확인해 보겠습니다.

  •   public static forRootAsync(
        options: HttpClientModuleAsyncOptions
      ): DynamicModule {
        const provider: Provider = {
          inject: [HTTP_CLIENT_MODULE_OPTIONS],
          provide: HTTP_CLIENT_TOKEN,
          useFactory: async (options: HttpClientModuleOptions) =>
            getHttpClientModuleOptions(options),
        };
    
        return {
          module: HttpClientModule,
          imports: options.imports,
          providers: [...this.createAsyncProviders(options), provider],
          exports: [provider],
        };
      }
    

    이제 우리가 알고 있듯이 옵션 개체는 이러한 다른 유형일 수 있으므로 처리해야 합니다.

      private static createAsyncProviders(
        options: HttpClientModuleAsyncOptions
      ): Provider[] {
        if (options.useExisting || options.useFactory) {
          return [this.createAsyncOptionsProvider(options)];
        }
    
        const useClass = options.useClass as Type<HttpClientModuleFactory>;
    
        return [
          this.createAsyncOptionsProvider(options),
          {
            provide: useClass,
            useClass,
          },
        ];
      }
    


    모든 이름 옵션이 있는 HttpClientModuleAsyncOptions도 살펴보겠습니다.

    
    export interface HttpClientModuleOptions {
      apiUrl: string;
      apiKey: string;
    }
    
    export interface HttpClientModuleFactory {
      createHttpModuleOptions: () =>
        | Promise<HttpClientModuleOptions>
        | HttpClientModuleOptions;
    }
    
    export interface HttpClientModuleAsyncOptions
      extends Pick<ModuleMetadata, "imports"> {
      inject?: any[];
      useClass?: Type<HttpClientModuleFactory>;
      useExisting?: Type<HttpClientModuleFactory>;
      useFactory?: (
        ...args: any[]
      ) => Promise<HttpClientModuleOptions> | HttpClientModuleOptions;
    }
    


    이러한 모든 준비가 완료되면 이 모듈을 다음과 같이 다양한 방식으로 사용할 수 있습니다.

    HttpClientModule.forRootAsync({
          imports: [AppConfigModule],
          inject: [AppConfigService],
          useFactory: (config: AppConfigService) => ({
            apiUrl: config.platformApi.baseUrl,
            apiKey: config.platformApi.apiKey,
          }),
        })
    


    다른 옵션




    @Module({
      imports: [HttpClientModule.forRootAsync({
        useExisting: AppConfigService
      })]
    })
    We could expect a dynamic module to be constructed with the following properties:
    {
      module: HttpClientModule,
      imports: [],
      providers: [
        {
          provide: HTTP_CLIENT_MODULE_OPTIONS,
          useFactory: async (optionsFactory: HttpClientModuleFactory) =>
            await optionsFactory.createHttpModuleOptions(),
          inject,
        },
      ],
    }
    


    결론



    패턴은 @nestjs/jwt, @nestjs/passport 및 @nestjs/typeorm과 같은 모든 인기 있는 모듈에서 사용됩니다. 이제 이러한 패턴이 얼마나 강력한지 뿐만 아니라 자신의 프로젝트에서 어떻게 사용할 수 있는지 알게 되기를 바랍니다.

    참조



  • https://docs.nestjs.com/fundamentals/dynamic-modules
  • https://docs.nestjs.com/
  • 원래 tkssharma.com에 게시된 블로그
  • 좋은 웹페이지 즐겨찾기