NestJS TypeORM Setup
TypeORM is a TypeScript-friendly ORM (Object Relational Mapper) that connects your NestJS application to a relational database like PostgreSQL, MySQL, or SQLite. An ORM lets you work with database tables as TypeScript classes and rows as objects — you write TypeScript instead of SQL for most database operations.
What an ORM Does
Without an ORM, you write raw SQL queries as strings inside your application code. With TypeORM, you define a class (called an entity) that maps to a database table, and TypeORM generates the SQL behind the scenes.
Without ORM:
"SELECT * FROM users WHERE id = 42"
With TypeORM:
this.userRepository.findOne({ where: { id: 42 } })
The TypeORM approach is type-safe, readable, and much less error-prone than raw SQL strings. TypeScript catches typos in property names and return types before the code runs.
Installing TypeORM for NestJS
npm install @nestjs/typeorm typeorm pg
@nestjs/typeorm— the NestJS wrapper around TypeORMtypeorm— the core TypeORM librarypg— the PostgreSQL driver (replace withmysql2for MySQL orbetter-sqlite3for SQLite)
Connecting to the Database
Import TypeOrmModule in your root module (AppModule) with your database connection settings:
// app.module.ts
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'yourpassword',
database: 'myapp',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true, // Auto-creates tables from entities (dev only)
}),
],
})
export class AppModule {}
Connection Configuration Explained
Setting | Meaning -----------------|-------------------------------------------- type | Database engine (postgres, mysql, sqlite) host | Database server address port | Database port (5432 for postgres) username/password| Database credentials database | Name of the database to connect to entities | Pattern to find entity files automatically synchronize | Auto-update schema on startup (DEV only)
Important: synchronize in Production
The synchronize: true option drops and recreates tables when your entities change. This is convenient during development but dangerous in production — it can delete your data. In production, use database migrations instead and set synchronize: false.
Database Setup Diagram
NestJS App
┌─────────────────────────────────────┐
│ AppModule │
│ TypeOrmModule.forRoot(config) │
│ | │
│ v │
│ TypeORM creates a connection pool │
│ | │
│ v │
│ Entities register as DB tables │
└─────────────────────────────────────┘
|
v
PostgreSQL Database
┌──────────────┐
│ users table │
│ posts table │
│ orders table│
└──────────────┘
Using Environment Variables for Config
Hard-coding database credentials in AppModule is insecure. Use environment variables instead:
TypeOrmModule.forRoot({
type: 'postgres',
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT),
username: process.env.DB_USER,
password: process.env.DB_PASS,
database: process.env.DB_NAME,
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: process.env.NODE_ENV !== 'production',
}),
Store actual values in a .env file (excluded from version control via .gitignore). Never commit database passwords to your code repository.
TypeOrmModule.forRootAsync
When you use the NestJS ConfigModule to manage environment variables, you need the async version to wait for the config service before connecting:
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
type: 'postgres',
host: configService.get('DB_HOST'),
port: configService.get<number>('DB_PORT'),
username: configService.get('DB_USER'),
password: configService.get('DB_PASS'),
database: configService.get('DB_NAME'),
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: false,
}),
inject: [ConfigService],
}),
Registering Entities in Feature Modules
Each entity must register in the feature module that uses it. This makes the entity's repository injectable inside that module:
// users.module.ts
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';
@Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}
After this setup, you can inject the UserRepository into UsersService using the @InjectRepository(User) decorator. Repositories are covered in detail in the next topic.
SQLite for Quick Development
For prototyping without installing a full database server, SQLite stores the entire database in a single file:
npm install better-sqlite3
TypeOrmModule.forRoot({
type: 'better-sqlite3',
database: 'dev.sqlite',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
})
The database file (dev.sqlite) appears in your project folder and requires no installation or configuration. Switch to PostgreSQL or MySQL when you deploy to production.
TypeORM setup is a one-time configuration step. Once the connection is established and entities are registered, every feature module gains access to a type-safe database layer without any additional connection management.
