TypeORM Setup
TypeORM is the most widely used ORM in the NestJS ecosystem, and the official @nestjs/typeorm package wraps it in a dependency-injection-friendly module. With a single TypeOrmModule.forRoot call you describe your data source — driver, host, credentials, entities — and Nest opens a connection pool during bootstrap and tears it down on shutdown. This page walks through installing the packages, configuring the connection synchronously and asynchronously, and verifying that the database is actually reachable when your app starts.
Installing the packages
You need three things: the Nest integration, the TypeORM core library, and a driver for your database. For PostgreSQL you install pg; swap it for mysql2, better-sqlite3, or mssql depending on your engine.
npm install @nestjs/typeorm typeorm pg
TypeORM relies on decorator metadata, so make sure
experimentalDecoratorsandemitDecoratorMetadataare both enabled intsconfig.json. The default Nest CLI project already sets these.
Configuring the connection with forRoot
The simplest setup passes a plain options object to TypeOrmModule.forRoot in your root module. Nest registers the data source as a provider, so any repository or the DataSource itself can be injected anywhere in the app.
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './users/user.entity';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'postgres',
database: 'app',
entities: [User],
synchronize: false,
}),
],
})
export class AppModule {}
The entities array tells TypeORM which classes map to tables. Rather than listing each class by hand, most projects use a glob so new entities are picked up automatically:
entities: [__dirname + '/**/*.entity{.ts,.js}'],
Never enable
synchronize: truein production. It auto-alters your schema to match entities and will silently drop columns and data. Use migrations instead.
Common data source options
These are the options you will reach for most often when wiring up forRoot.
| Option | Type | Purpose |
|---|---|---|
type | string | Database driver: postgres, mysql, sqlite, mssql, etc. |
host / port | string / number | Server location for networked databases. |
entities | array | Entity classes or glob patterns to register. |
synchronize | boolean | Auto-create schema on boot. Dev only. |
autoLoadEntities | boolean | Auto-register entities from forFeature calls. |
logging | boolean / array | Log queries, errors, or specific levels. |
retryAttempts | number | How many times to retry the initial connect (default 10). |
Async configuration with forRootAsync
In real applications the connection details come from environment variables, not hard-coded strings. Use forRootAsync to read configuration from the ConfigService (or any injected provider) at startup.
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
TypeOrmModule.forRootAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
type: 'postgres',
host: config.get<string>('DB_HOST', 'localhost'),
port: config.get<number>('DB_PORT', 5432),
username: config.get<string>('DB_USER'),
password: config.get<string>('DB_PASS'),
database: config.get<string>('DB_NAME'),
autoLoadEntities: true,
synchronize: config.get('NODE_ENV') !== 'production',
}),
}),
],
})
export class AppModule {}
Setting autoLoadEntities: true means any entity registered through TypeOrmModule.forFeature([...]) in a feature module is added to the data source automatically, so you can drop the global entities glob entirely.
Verifying the connection on bootstrap
By default Nest retries the connection up to ten times and then lets the app start even if the database is down. During development you usually want to fail fast instead, so inject the DataSource and confirm it initialized.
import { Injectable, Logger, OnApplicationBootstrap } from '@nestjs/common';
import { InjectDataSource } from '@nestjs/typeorm';
import { DataSource } from 'typeorm';
@Injectable()
export class DatabaseHealthService implements OnApplicationBootstrap {
private readonly logger = new Logger(DatabaseHealthService.name);
constructor(@InjectDataSource() private readonly dataSource: DataSource) {}
async onApplicationBootstrap(): Promise<void> {
if (this.dataSource.isInitialized) {
const { database } = this.dataSource.options as { database?: string };
this.logger.log(`Connected to database "${database}"`);
} else {
this.logger.error('Data source failed to initialize');
}
}
}
Register the service in a module’s providers, then start the app:
npm run start:dev
Output:
[Nest] 4821 - 06/14/2026, 9:02:14 AM LOG [InstanceLoader] TypeOrmModule dependencies initialized +3ms
[Nest] 4821 - 06/14/2026, 9:02:14 AM LOG [DatabaseHealthService] Connected to database "app"
[Nest] 4821 - 06/14/2026, 9:02:14 AM LOG [NestApplication] Nest application successfully started +6ms
If the credentials are wrong you will instead see TypeORM’s retry messages followed by a connection error, which is your signal to check the .env values or that the database server is running.
Best practices
- Keep
synchronizeoff in production and manage schema changes through migrations. - Load all connection settings from environment variables via
forRootAsyncandConfigService— never commit credentials. - Prefer
autoLoadEntities: truewith per-featureforFeatureregistration over a single global entity glob. - Enable
logging: ['error', 'warn']in non-production environments to catch slow or failing queries early. - Tune
retryAttemptsandretryDelayso the app waits sensibly for the database in containerized startups. - Inject the
DataSourceto run a health check on bootstrap so misconfiguration surfaces immediately.