Skip to content
NestJS ns database 4 min read

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: true against 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.

LibraryTypeDatabasesSchema sourceBest for
TypeORMORMPostgres, MySQL, SQLite, MSSQL, OracleDecorator entitiesConventional relational apps, deep Nest integration
PrismaORM (query engine)Postgres, MySQL, SQLite, MongoDB, othersschema.prisma fileType safety, generated client, DX
MongooseODMMongoDBSchema classesDocument/MongoDB workloads
SequelizeORMPostgres, MySQL, SQLite, MSSQLDecorator modelsTeams already invested in Sequelize
MikroORMORM (Data Mapper)Postgres, MySQL, SQLite, MongoDBDecorator entitiesUnit-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 from ConfigService, never hard-coded strings.
  • Keep synchronize/autoSync off 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.
Last updated June 14, 2026
Was this helpful?