Database Integration Overview
NestJS is database-agnostic: it ships no built-in persistence layer and instead provides thin, well-maintained integration packages that wire popular ORMs and ODMs into the framework’s dependency injection container. That means you keep using each library’s idiomatic API, while Nest handles connection lifecycle, module configuration, and injectable repositories. Choosing the right tool up front matters because it shapes your data modeling, your testing story, and how much SQL or query-building you write by hand.
How NestJS integrates a data layer
Every official integration follows the same pattern: a root module configured once with forRoot() (or forRootAsync() for config-driven setups), and feature modules that register their models with forFeature(). The integration then exposes injectable providers — repositories, models, or a client — that you pull into services through the constructor. This keeps connection management out of your business logic and makes data access mockable in tests.
// app.module.ts
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',
url: config.getOrThrow<string>('DATABASE_URL'),
autoLoadEntities: true,
synchronize: false,
}),
}),
],
})
export class AppModule {}
Never set
synchronize: trueagainst a production database. It silently alters your schema to match entities and can drop columns. Use migrations instead.
The main options
NestJS has first-party packages for TypeORM, Sequelize, and Mongoose (@nestjs/typeorm, @nestjs/sequelize, @nestjs/mongoose). Prisma and MikroORM integrate cleanly too — Prisma through a small custom provider, and MikroORM via its own community-maintained @mikro-orm/nestjs package.
| Library | Type | Databases | Schema source | Best for |
|---|---|---|---|---|
| TypeORM | ORM | Postgres, MySQL, SQLite, MSSQL, Oracle | Decorator entities | Conventional relational apps, deep Nest integration |
| Prisma | ORM (query engine) | Postgres, MySQL, SQLite, MongoDB, others | schema.prisma file | Type safety, generated client, DX |
| Mongoose | ODM | MongoDB | Schema classes | Document/MongoDB workloads |
| Sequelize | ORM | Postgres, MySQL, SQLite, MSSQL | Decorator models | Teams already invested in Sequelize |
| MikroORM | ORM (Data Mapper) | Postgres, MySQL, SQLite, MongoDB | Decorator entities | Unit-of-work, identity map, strict typing |
TypeORM
TypeORM is the most common choice in the Nest ecosystem. It uses decorator-based entities and an injectable Repository<T> per entity, which maps naturally onto Nest’s DI.
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private readonly users: Repository<User>,
) {}
findActive(): Promise<User[]> {
return this.users.find({ where: { active: true } });
}
}
Prisma
Prisma defines its schema in a dedicated schema.prisma file and generates a fully typed client. There is no official Nest package because integration is trivial: wrap the generated client in an injectable service.
import { Injectable, OnModuleInit } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
async onModuleInit(): Promise<void> {
await this.$connect();
}
}
Its standout feature is end-to-end type inference — query results are typed from the schema, so a missing field is a compile error rather than a runtime surprise.
Mongoose
For MongoDB, Mongoose is the de-facto ODM. @nestjs/mongoose lets you define schemas as TypeScript classes and injects a typed Model<T>.
@Injectable()
export class CatsService {
constructor(@InjectModel(Cat.name) private readonly catModel: Model<Cat>) {}
create(dto: CreateCatDto): Promise<Cat> {
return new this.catModel(dto).save();
}
}
Sequelize and MikroORM
Sequelize is a mature relational ORM with decorator models via sequelize-typescript; pick it when a team already knows it. MikroORM brings a Data Mapper architecture with a unit-of-work and identity map — closer to JPA/Hibernate semantics — and appeals to teams wanting explicit flushing and strong consistency guarantees.
Choosing one
Match the tool to the data store and the team, not the hype.
- Use Mongoose when your store is MongoDB and you think in documents.
- Use Prisma when you want the strongest type safety and the smoothest developer experience on a relational database.
- Use TypeORM when you want the most batteries-included Nest experience and the active-record/repository style.
- Use Sequelize or MikroORM when your team’s existing expertise or architectural preferences point there.
Output:
$ npx prisma generate
Generated Prisma Client (v6) in ./node_modules/@prisma/client in 187ms
Best practices
- Configure the connection once with
forRootAsync()and pull credentials fromConfigService, never hard-coded strings. - Keep
synchronize/autoSyncoff outside local development and manage schema changes with migrations. - Inject repositories, models, or the client into services — keep controllers free of data-access calls.
- Standardize on one persistence library per service to avoid duplicated transaction and connection logic.
- Use connection pooling and tune pool size to your deployment’s concurrency; the defaults are rarely right for production.
- Wrap multi-step writes in transactions so partial failures roll back cleanly.