Intro
We usually have different environments: local, dev, prod etc. (or test, staging..) Each of the environment has its own separate database, and connect to different services with different credentials.
Environment variables are used to hide confidential information and to provide a layer of customization on different environments like dev and production.
Let’s take a look at how to use env vars with Nest.js + TypeORM together.
Nest.js comes with a config system:
The official complete doc https://docs.nestjs.com/techniques/configuration is recommended but in this tutorial we will just provide essentials of our project for now.
TypeORM also comes with its own config system and supports env vars(https://orkhan.gitbook.io/typeorm/docs/using-ormconfig), but I would recommend to have a single source of truth and config the settings in one place.
(Credits to this answer on StackOverflow https://stackoverflow.com/questions/59913475/configure-typeorm-with-one-configuration-for-cli-and-nestjs-application)
Env vars
Create .env.local
for local development, also please consider add this file to .gitignore
# .env.local
DB_HOST=localhost
DB_PORT=5432
DB_USER=sample_nestjs_user
DB_PASSWORD=sample_nestjs_password
DB_DATABASE=sample_nestjs_db
Optionally you can configure similarly for dev or prod environments
like for dev
# .env.dev
DB_HOST=dev.some-dbhost.com
DB_PORT=5432
DB_USER=dev_db_user
DB_PASSWORD=dev_db_password
DB_DATABASE=dev_db
For production please declare the value of env vars directly in e.g. console panel of AWS or similar
Nest.js Config Module
Install Nest.js Config Module
npm i --save @nestjs/config
Import Config Module
// app.module.ts
ConfigModule.forRoot({
isGlobal: true,
envFilePath: [`.env.${process.env.NODE_ENV}`],
}),
Then, we can specify the NODE_ENV=local
to start using .env.local
// package.json
{
"scripts": {
"start:local": "NODE_ENV=local nest start --watch"
}
}
DB config to be used in both Nest.js and TypeORM (& CLI)
Create the config, in namespace database
(or whatever name you want)
// src/config/db.config.ts
import { registerAs } from '@nestjs/config';
import { SnakeNamingStrategy } from 'typeorm-naming-strategies';
export default registerAs('database', () => ({
type: 'postgres',
host: process.env.DB_HOST,
port: process.env.DB_PORT,
database: process.env.DB_DATABASE,
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
logging: true,
logger: 'file',
entities: ['./dist/**/*entity.{ts,js}'],
migrationsTableName: 'migrations',
migrations: ['dist/**/migrations/*.{ts,js}'],
synchronize: process.env.NODE_ENV === 'local',
cli: {
migrationsDir: `src/infrastructure/database/migrations`,
},
namingStrategy: new SnakeNamingStrategy(),
}));
That’s how we use env vars instead of hardcoding the credentials or configs.
In this tutorial we also added more configs on TypeORM. We will explain how synchorinze
works and introduce the concept of DB migrations in later chapters. For naming strategy we will keep it consistent with Postgres.
We will import the DB config both into Nest.js and TypeORM.
In Nest.js ‘s Config Module load the config and in TypeORM module:
// app.module.ts
import dbConfig from 'src/config/db.config';
ConfigModule.forRoot({
isGlobal: true,
load: [dbConfig],
envFilePath: [`.env.${process.env.NODE_ENV}`],
}),
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
...(await configService.get('database')),
}),
inject: [ConfigService],
}),
In this way it gets database config injected.
TypeORM CLI
To use the same config with env vars in TypeORM CLI (e.g. to run DB migrations):
Create ormconfig.ts
in root folder
// ormconfig.ts
import { ConfigModule } from '@nestjs/config';
import dbConfig from 'src/config/db.config';
ConfigModule.forRoot({
isGlobal: true,
load: [dbConfig],
envFilePath: [`.env.${process.env.NODE_ENV}`],
});
export default dbConfig();
In this way it uses the Config Module from Nest.js again, executes in env context and provides the result config to TypeORM’s own config.
To add NODE_ENV
to TypeORM CLI commands
// package.json
{
"scripts": {
"typeorm": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js",
"typeorm:local": "NODE_ENV=local ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js",
"typeorm:dev": "NODE_ENV=dev ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js",
"typeorm:migration:generate": "npm run typeorm -- migration:generate -n",
"typeorm:migration:generate:dev": "npm run typeorm:dev -- migration:generate -n",
"typeorm:migration:run": "npm run typeorm -- migration:run",
"typeorm:migration:run:dev": "npm run typeorm:dev -- migration:run"
}
}