#"@inject" Namespace Configuration: Nest Can't Resolve Dependencies

10 messages · Page 1 of 1 (latest)

proven veldt
#

I am trying to set up a TypeORM module using forRootAsync. Inside it, I am using the useClass property and passing my custom class TypeOrmConfigService.

The issue I am having is that I am trying to pass a namespace for a configuration inside it that contains my database configurations. However, when I run it, it fails to bootstrap and says it can't resolve my namespace config.

I tried including the configModule inside the array of the imports property, but it still fails. According to the documentation, the configModule is not global and needs to be imported into each module (unless defined globally).

So, I think the issue lies in the core module of the @nestjs/typeorm package, which doesn't import the configModule and can't resolve the namespaces. However, I have no idea how to pass it to the service or module, and the documentation doesn't seem to provide any hints or information about it.

Below is my code, and any help or advice would be much appreciated. 🙂

#

app.module.ts:

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TypeOrmConfigService } from './typeorm.config.service';
import databaseConfig from './config/database.config';
import * as Joi from 'joi';

@Module({
  imports: [
    ConfigModule.forRoot({
      cache: true,
      validationSchema: Joi.object({
        NODE_ENV: Joi.string()
          .valid('development', 'production', 'test')
          .required(),
        API_PORT: Joi.number().default(3000),
        DB_HOST: Joi.string().required(),
        DB_PORT: Joi.number().required(),
        DB_USER: Joi.string().required(),
        DB_PASS: Joi.string().required(),
        DB_NAME: Joi.string().required(),
        DB_AUTO_LOAD_ENTITIES: Joi.boolean().required(),
      }),
      load: [databaseConfig],
    }),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      useClass: TypeOrmConfigService,
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
#

config/database.config.ts:

import { registerAs } from '@nestjs/config';

export default registerAs('database', () => ({
  host: process.env.DB_HOST,
  port: parseInt(process.env.DB_PORT, 10) || 5432,
  username: process.env.DB_USER,
  password: process.env.DB_PASS,
  database_name: process.env.DB_NAME,
  autoLoadEntities: process.env.DB_AUTO_LOAD_ENTITIES === 'true',
}));
#

typeorm.config.service.ts:

import { Inject, Injectable } from '@nestjs/common';
import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from '@nestjs/typeorm';
import databaseConfig from './config/database.config';
import { ConfigType } from '@nestjs/config';

@Injectable()
export class TypeOrmConfigService implements TypeOrmOptionsFactory {
  constructor(
    @Inject(databaseConfig.KEY)
    private dbConfig: ConfigType<typeof databaseConfig>,
  ) {}

  createTypeOrmOptions(
    connectionName?: string,
  ): TypeOrmModuleOptions | Promise<TypeOrmModuleOptions> {
    return {
      type: 'postgres',
      host: this.dbConfig.host,
      port: this.dbConfig.port,
      username: this.dbConfig.username,
      password: this.dbConfig.password,
      database: this.dbConfig.database_name,
      autoLoadEntities: this.dbConfig.autoLoadEntities,
      synchronize: false,
    };
  }
}
#

ERROR:

[Nest] 46275  - 05/02/2023, 12:09:09 PM   ERROR [ExceptionHandler] Nest can't resolve dependencies of the TypeOrmConfigService (?). Please make sure that the argument CONFIGURATION(database) at index [0] is available in the TypeOrmCoreModule context.

Potential solutions:
- Is TypeOrmCoreModule a valid NestJS module?
- If CONFIGURATION(database) is a provider, is it part of the current TypeOrmCoreModule?
- If CONFIGURATION(database) is exported from a separate @Module, is that module imported within TypeOrmCoreModule?
  @Module({
    imports: [ /* the Module containing CONFIGURATION(database) */ ]
  })
spring marsh
#

If you're injecting a namespaced config, you need to import the namespace with .forFeature

import databaseConfig from './config/database.config'
///
TypeOrmModule.forRootAsync({
  imports: [ConfigModule.forFeature(DatabaseConfig)],
  useClass: TypeOrmConfigService,
}),

(btw, personally, I would use a named export instead of a default one)

proven veldt
#

Thank you very much. That was the solution. I do have one question, though. What is the difference between an namespace configuration like this and a named export?

spring marsh
# proven veldt Thank you very much. That was the solution. I do have one question, though. What...

they are two completely different things.

Namespace configuration is a feature of nestjs/config which lets you modularize your configuration and inject only that part which is needed.

Named and default exports are a features of typescript.
If you do

// hello.ts
export default 'hello'

then the consuming file can name that whichever it wants

// index.ts
import helloText from 'hello'
console.log(helloText)

If you export it under a name

export const hello = 'hello'

then the consuming file must import it under that defined name

// index.ts
import { hello } from 'hello'
console.log(hello)
proven veldt
#

I see, thanks for the explanation. Honestly, I was mostly using this setup because I had never used namespaces in Nest before and wanted to give it a try. Thanks for all the help though. 🙂

spring marsh
#

What I was suggesting was using this syntax instead

export const databaseConfig = registerAs('database', () => ({

Purely for aestehetic reasons 😉 and also the IDE usually does a better job auto-importing named exports than default ones