This episode covers database integration in NestJS: choosing TypeORM, Sequelize, or Prisma; defining entities and migrations; using the repository pattern and query builder; and in-memory databases for development and testing.

Real applications almost always need to store data. NestJS provides official integration for various ORMs, and this episode will guide you in choosing and using one. We'll focus on TypeORM because it's the most common, then touch on Prisma and Sequelize.
With data access mastered, you can build applications that are truly useful — not just endpoints returning static data.
These three ORMs are the main choices in the NestJS ecosystem:
For this series we use TypeORM because its integration with NestJS decorators is the smoothest.
npm install @nestjs/typeorm typeorm sqlite3For PostgreSQL, replace sqlite3 with pg. The @nestjs/typeorm package is the official wrapper that integrates TypeORM into NestJS.
Add the configuration to the root module:
import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
@Module({
imports: [
TypeOrmModule.forRoot({
type: "sqlite",
database: "data.sqlite",
autoLoadEntities: true,
synchronize: true,
}),
],
})
export class AppModule {}autoLoadEntities: true automatically registers entities from feature modules, and synchronize: true syncs the schema with the database — fine for development, don't use it in production.
An entity is a class that represents a table:
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
@Entity("users")
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column({ unique: true })
email: string;
}The @Entity("users") decorator maps the class to the users table, @PrimaryGeneratedColumn for the auto-increment primary key, and @Column for columns.
synchronize is only for development. For production, use migrations that generate the schema in a controlled way:
npm run typeorm -- migration:generate src/migrations/Init --dataSource src/data-source.tsMigrations keep a history of schema changes so they can be applied to every environment consistently and can be rolled back.
TypeORM provides the repository pattern via @InjectRepository:
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { User } from "./user.entity";
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private readonly usersRepository: Repository<User>,
) {}
create(name: string, email: string): Promise<User> {
const user = this.usersRepository.create({ name, email });
return this.usersRepository.save(user);
}
findAll(): Promise<User[]> {
return this.usersRepository.find();
}
}Repositories provide ready-to-use methods: find, findOne, save, update, delete, and many more. To make the repository injectable, register the entity via TypeOrmModule.forFeature([User]) in the feature module's imports array.
For complex queries, TypeORM provides the query builder:
async findActiveByName(name: string): Promise<User[]> {
return this.usersRepository
.createQueryBuilder("user")
.where("user.name LIKE :name", { name: `%${name}%` })
.andWhere("user.isActive = :active", { active: true })
.orderBy("user.id", "DESC")
.take(10)
.getMany();
}The query builder gives you full control over the generated SQL: joins, subqueries, aggregations, and pagination. The :name parameter prevents SQL injection because it uses parameter binding.
For testing, an in-memory database is very practical because it never touches the disk:
TypeOrmModule.forRoot({
type: "sqlite",
database: ":memory:",
autoLoadEntities: true,
synchronize: true,
})The value ":memory:" makes the database run in RAM and disappear when the process ends — perfect for fast, isolated unit tests.
Episode 6 takes you from zero to data actually being stored: choosing an ORM, TypeORM configuration, defining entities, migrations, the repository pattern, the query builder, and in-memory databases.
Key takeaways:
TypeOrmModule.forRoot configures the database connection.synchronize is only for development; production uses migrations.@InjectRepository.In the next episode 7 we'll discuss validation and exception handling — validation with class-validator and class-transformer, global pipes, custom validation pipes, exception filters, and standardizing error responses.