Nikaple / nest-typed-config

Intuitive, type-safe configuration module for Nest framework ✨
MIT License
204 stars 25 forks source link

How to: async subset config sub modules? #404

Closed bneigher closed 1 year ago

bneigher commented 1 year ago

I'm submitting a...

Desired behavior

This might be a general nestjs question, but I figured I would ask here as this is probably a use case others may have. Basically, I am developing an App -> Lib relationship with strict config checking (to prevent developers missing setting an environment variable).

The requirement here is that the App needs to fetch a bunch of variables from external source (AWS Secret Manager). This works fine and the Config is type checked after loading. However, I have a library that depends on some of the configs (a subset). I want there to be validation on the library's module as well. But I can't figure out how to get the library to only load after the App has loaded.. I'm getting undefined values for my config values within the library getting booted up. See illustration below:

AppModule

import { getSecret } from '@aws-lambda-powertools/parameters/secrets'
import { Config } from 'app/config'
/* 
  public readonly VAR_1!: string
  public readonly VAR_2!: string
  public readonly VAR_3!: string
  public readonly VAR_4!: string
*/

@Module({
  imports: [
    TypedConfigModule.forRootAsync({
      isGlobal: true,
      schema: Config,
      load: [
        dotenvLoader(),
        async () => {
          const secret = await getSecret('my-secret', {
            transform: 'json',
          })
          return secret
        },
      ],
    }),
    ServiceModule.registerAsync({
      inject: [Config],
    }),
  ],
  providers: [AppService],
})
export class AppModule {}

and then in my lib (ServiceModule):

import { HttpModule, HttpModuleAsyncOptions, HttpService } from '@nestjs/axios'
import { Config } from 'lib/service/config'
/* 
  public readonly VAR_3!: string
  public readonly VAR_4!: string
*/

export const SERVICE_API_REST = 'SERVICE_API_REST'

@Module({})
export class ServiceModule {
  static registerAsync(options: any): DynamicModule {
    return {
      module: CoreSVCSModule,
      imports: [
        HttpModule.registerAsync({
          inject: [Config],
          useFactory: async (config: Config) => {
            return {
              url: config.VAR_3,
              headers: {
                'Content-Type': 'application/json',
                'api-key': config.VAR_4,
              },
            }
          },
        } as HttpModuleAsyncOptions),
      ],
      providers: [
        {
          provide: SERVICE_API_REST,
          inject: [HttpService],
          useFactory: (client: HttpService) => client,
        },
      ],
      exports: [SERVICE_API_REST],
    }
  }
}

How can I change this to work as desired?

What is the motivation / use case for changing the behavior?

Illustration of developing libraries that share configs from a parent app, enforcing the same checks and informing developers which configs are needed from parent.

Environment


Nest version: 10
Nest-Typed-Config version: 2.6.0

For Tooling issues:
- Node version: 20
- Platform:  Mac

Others:
nx monorepo            
Nikaple commented 1 year ago

I didn't quite understand the question, but I have provided a codesandbox and a few suggestions:

  1. If you want to create a library's configuration based on the application's configuration while keeping the validation decorators, you can define the application's configuration based on the library's configuration. However, you need to give a new namespace to the library's configuration. If importing the application's module in the library seems like bad code to you, you can lift the configuration itself to another library.
  2. The second point is not applicable to the problem at hand. Injecting configuration from ServiceModule.registerAsync works fine, and Config does not resolve to undefined. This part of the problem has not been reproduced. Please provide further details with codesandbox if you have additional questions.