Skip to content
NestJS ns database 4 min read

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 experimentalDecorators and emitDecoratorMetadata are both enabled in tsconfig.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: true in 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.

OptionTypePurpose
typestringDatabase driver: postgres, mysql, sqlite, mssql, etc.
host / portstring / numberServer location for networked databases.
entitiesarrayEntity classes or glob patterns to register.
synchronizebooleanAuto-create schema on boot. Dev only.
autoLoadEntitiesbooleanAuto-register entities from forFeature calls.
loggingboolean / arrayLog queries, errors, or specific levels.
retryAttemptsnumberHow 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 synchronize off in production and manage schema changes through migrations.
  • Load all connection settings from environment variables via forRootAsync and ConfigService — never commit credentials.
  • Prefer autoLoadEntities: true with per-feature forFeature registration over a single global entity glob.
  • Enable logging: ['error', 'warn'] in non-production environments to catch slow or failing queries early.
  • Tune retryAttempts and retryDelay so the app waits sensibly for the database in containerized startups.
  • Inject the DataSource to run a health check on bootstrap so misconfiguration surfaces immediately.
Last updated June 14, 2026
Was this helpful?