Skip to content

NestJS Databases

Most NestJS applications eventually need persistent storage. This lesson surveys how Nest integrates with SQL and NoSQL databases, and how repositories keep persistence behind a clean interface.

How NestJS Talks to Databases

NestJS does not ship its own database engine. Instead it integrates with community ORMs and ODM drivers through official packages and modules: TypeORM for SQL, Prisma as a type-safe query layer, Mongoose for MongoDB, and others like Sequelize or MikroORM.

The usual pattern is a feature module that imports a database module, injects a repository or Prisma client into a service, and keeps controllers free of raw query logic.

@Module({
  imports: [
    TypeOrmModule.forRoot({ type: 'postgres', url: process.env.DATABASE_URL }),
    TypeOrmModule.forFeature([User]),
  ],
  providers: [UsersService],
  controllers: [UsersController],
})
export class UsersModule {}

forRoot() configures the connection once at the application level; forFeature() registers entity repositories for a specific feature module.

Choosing a Persistence Approach

SQL (Postgres/MySQL)  -> TypeORM or Prisma
MongoDB               -> Mongoose
Multi-database apps   -> dynamic modules + repository abstraction
Raw SQL needed        -> Prisma / TypeORM query builder / driver
  • Pick the ORM that matches your team's SQL or document comfort, not just Nest advertising.
  • Always inject database clients as providers, never import a global singleton from a utility file.
  • Keep migrations and schema changes in version control alongside application code.
  • Isolate vendor-specific query code behind repository interfaces so you can swap ORM later.

NestJS Database Options

Official and common persistence stacks used with Nest.

Stack Package Best For
TypeORM @nestjs/typeorm SQL apps wanting Active Record or Data Mapper
Prisma @prisma/client + custom provider Type-safe SQL/Postgres with generated client
Mongoose @nestjs/mongoose MongoDB document models
Sequelize @nestjs/sequelize Teams already on Sequelize
MikroORM @mikro-orm/nestjs Unit-of-work Data Mapper apps

Connection Lifecycle

Database connections should be established during application bootstrap and closed cleanly on shutdown. Nest module onModuleInit / onModuleDestroy hooks and the ORM's own Nest integrations usually handle this if you use the official packages.

  • Never open a new connection per request.
  • Use connection pooling for SQL databases in production.
  • Provide connection strings via environment variables, not hard-coded config.

Transactions Across Services

When creating related records (an order plus order items), wrap writes in a transaction. TypeORM's QueryRunner, Prisma's $transaction, and Mongoose sessions each have Nest-friendly patterns you'll see in the dedicated ORM lessons.

Common Mistakes

  • Injecting the raw database connection into controllers instead of a service/repository.
  • Leaving default credentials in committed config files.
  • Mixing multiple ORM styles in one feature without a clear boundary.
  • Skipping migrations and relying on auto-sync schema options in production.

Key Takeaways

  • Nest integrates with TypeORM, Prisma, Mongoose, and other ORMs via modules and providers.
  • Configure connections once with forRoot-style modules; register entities/models per feature.
  • Keep persistence behind repositories/services, not controllers.
  • Use environment-based connection config and proper pooling in production.

Pro Tip

Establish a repository interface early, even if your first implementation uses TypeORM, so replacing or wrapping persistence later does not rewrite every controller and service method signature.