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
- Verified ConfigModule setup:
isGlobal: trueis set - Added .js extensions to local imports (ESM requirement)
- Switched from ts-node to tsx due to loader deprecation warnings
- Used
registerAs()in config files for proper namespace registration - Confirmed module import order: ConfigModule is imported before DatabaseModule
- Added extensive debugging: ConfigService is definitely
undefinedin constructor
Questions
- Are there known issues with NestJS dependency injection in ESM mode, especially when forced to migrate for ESM-only packages?
- Is there a specific order or timing issue with how modules are loaded in ESM that affects DI?
- Should I be using different decorators or patterns for ESM compatibility with external ESM-only dependencies?
- Are there additional configuration steps needed for NestJS + ESM beyond the standard migration guide when integrating ESM-only packages?
- Has anyone successfully integrated
@openai/agents/realtimeor similar ESM-only packages with NestJS?