Configure TypeORM by Injecting NestJS Config
NestJS comes with nice integration with TypeORM and with a nice @nestjs/config package. But getting them to work well together using dependency injection was a bit of a trick.
The docs make it look easy, and in the end it does look easy. For some reason, the docs seem incomplete and the solution not obvious. Here's one solution that worked for us.
Configure TypeORM
TypeORM is a little ORM that works well with Nest. It's configured in the app.modules.ts
. One includes it in the imports
like so:
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'postgres',
database: 'test',
}),
],
})
export class AppModule {}
Values from Environment Variables
But one does not want to put static database connection strings into the committed code. Some of those things are secret or at least can change. So we want to pull them from the environment variables.
NestJS has a @nestjs/config package for that. Once it's setup, the NestJS docs taunt you with the nice usage of dependency-injected ConfigService:
const dbUser = this.configService.get<string>('DATABASE_USER');
The issue is how to get the ConfigService
available to the TypeORM config that's in the AppModule
decorator.
Injected ConfigService for TypeORM
Here's what worked for us, in app.module.ts
:
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
type: 'postgres' as 'postgres',
host: configService.get('DATABASE_HOST', 'localhost'),
port: configService.get<number>('DATABASE_PORT', 5432),
username: configService.get('DATABASE_USER', 'postgres'),
password: configService.get('DATABASE_PASS', 'postgres'),
database: configService.get('DATABASE_SCHEMA', 'prism'),
}),
}),
],
})
export class AppModule {}
A few things are important here that weren't readily apparent from my naive reading of the docs:
- The
type: 'postgres' as 'postgres'
cast is important to specify which polymorphic type of TypeOrmModuleOptions you're returning fromuseFactory
. import { ConfigService } from '@nestjs/config'
is the class that you need toinject
and is the type of theuseFactory
argument. The docs were unclear on where this was imported from or possibly custom-implemented.- The typing of number for
configService.get
and a number (5432) as fallback are important to get right as type for the() port
property, otherwise the TypeScript compiler is not happy with theuseFactory
implementation.
There are so many other examples on the web of people having to create their own ConfigService
implementations. The above solution avoids that (even though we ended up going to a custom implementation later ourselves in order to share config with migrations).
Hopefully this simple solution (which we hoped against hope existed!) saves someone some time.