[HELP] NestJS Failing After CommonJS to ESM Migration

I’m migrating my NestJS backend from CommonJS to ES Modules (ESM) and encountering a critical issue where ConfigService is not being properly injected, causing the application to crash during startup.

Migration Context: This migration is required because I need to integrate the @openai/agents/realtime package, which has co-dependencies that exclusively use ESM and cannot be imported in a CommonJS environment.

Error Details

[Nest] ERROR [ExceptionHandler] Cannot read properties of undefined (reading 'get')
TypeError: Cannot read properties of undefined (reading 'get')
    at DatabaseService constructor

When I added debugging, I confirmed: Error: ConfigService dependency injection failed

Environment

  • Node.js: 20.16.0
  • NestJS: 10.4.15
  • TypeScript: 5.5.4
  • Package manager: npm 10.8.1
  • Runtime: tsx (switched from ts-node due to ESM loader deprecation warnings)

package.json (relevant parts)

json

{
  "type": "module",
  "scripts": {
    "start:dev": "nodemon --watch src --exec \"tsx src/main.ts\""
  }
}

tsconfig.json

json

{
  "compilerOptions": {
    "module": "ESNext",
    "moduleResolution": "Node",
    "esModuleInterop": true,
    "target": "ESNext",
    // ... other options
  },
  "ts-node": {
    "esm": true
  }
}

app.module.ts

typescript

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';

import baseConfig from '@/config/base.config';
import databaseConfig from '@/config/knex.config';
// ... other config imports

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      load: [baseConfig, databaseConfig, /* other configs */],
      ignoreEnvFile: true,
    }),
    DatabaseModule,
    // ... other modules
  ],
})
export class AppModule {}

Failing Service

typescript

@Injectable()
export class DatabaseService implements OnModuleInit {
  constructor(private readonly configService: ConfigService) {
    // configService is undefined here
    if (!this.configService) {
      throw new Error('ConfigService dependency injection failed');
    }
    
    const databaseConfig = this.configService.get<DatabaseConfig>('database');
    // ...
  }
}

What I’ve Tried

  1. Verified ConfigModule setup: isGlobal: true is set
  2. Added .js extensions to local imports (ESM requirement)
  3. Switched from ts-node to tsx due to loader deprecation warnings
  4. Used registerAs() in config files for proper namespace registration
  5. Confirmed module import order: ConfigModule is imported before DatabaseModule
  6. Added extensive debugging: ConfigService is definitely undefined in constructor

Questions

  1. Are there known issues with NestJS dependency injection in ESM mode, especially when forced to migrate for ESM-only packages?
  2. Is there a specific order or timing issue with how modules are loaded in ESM that affects DI?
  3. Should I be using different decorators or patterns for ESM compatibility with external ESM-only dependencies?
  4. Are there additional configuration steps needed for NestJS + ESM beyond the standard migration guide when integrating ESM-only packages?
  5. Has anyone successfully integrated @openai/agents/realtime or similar ESM-only packages with NestJS?

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.