Database Integration Overview
NestJS provides comprehensive support for multiple databases, including relational databases (like MySQL, PostgreSQL) and non-relational databases (like MongoDB). By using ORM/ODM libraries such as TypeORM, Prisma, Mongoose, developers can easily implement database operations.
TypeORM Integration
Install Dependencies
bashnpm install @nestjs/typeorm typeorm npm install mysql2 # or pg, sqlite3, better-sqlite3, etc.
Configure TypeORM
typescriptimport { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { User } from './entities/user.entity'; @Module({ imports: [ TypeOrmModule.forRoot({ type: 'mysql', host: 'localhost', port: 3306, username: 'root', password: 'password', database: 'test', entities: [User], synchronize: true, // Should be false in production logging: true, }), TypeOrmModule.forFeature([User]), ], providers: [UserService], controllers: [UserController], }) export class UserModule {}
Create Entity
typescriptimport { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm'; @Entity('users') export class User { @PrimaryGeneratedColumn() id: number; @Column({ unique: true }) email: string; @Column() password: string; @Column() name: string; @Column({ default: true }) isActive: boolean; @CreateDateColumn() createdAt: Date; @UpdateDateColumn() updatedAt: Date; }
Create Repository
typescriptimport { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { User } from './entities/user.entity'; import { CreateUserDto } from './dto/create-user.dto'; @Injectable() export class UserService { constructor( @InjectRepository(User) private userRepository: Repository<User>, ) {} async create(createUserDto: CreateUserDto): Promise<User> { const user = this.userRepository.create(createUserDto); return this.userRepository.save(user); } async findAll(): Promise<User[]> { return this.userRepository.find(); } async findOne(id: number): Promise<User> { return this.userRepository.findOne({ where: { id } }); } async update(id: number, updateUserDto: any): Promise<User> { await this.userRepository.update(id, updateUserDto); return this.findOne(id); } async remove(id: number): Promise<void> { await this.userRepository.delete(id); } async findByEmail(email: string): Promise<User> { return this.userRepository.findOne({ where: { email } }); } }
Prisma Integration
Install Dependencies
bashnpm install @prisma/client npm install prisma --save-dev npx prisma init
Configure Prisma
prisma// prisma/schema.prisma datasource db { provider = "mysql" url = env("DATABASE_URL") } generator client { provider = "prisma-client-js" } model User { id Int @id @default(autoincrement()) email String @unique password String name String isActive Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }
Create Prisma Service
typescriptimport { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common'; import { PrismaClient } from '@prisma/client'; @Injectable() export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy { async onModuleInit() { await this.$connect(); } async onModuleDestroy() { await this.$disconnect(); } }
Use Prisma
typescriptimport { Injectable } from '@nestjs/common'; import { PrismaService } from './prisma.service'; import { CreateUserDto } from './dto/create-user.dto'; @Injectable() export class UserService { constructor(private prisma: PrismaService) {} async create(createUserDto: CreateUserDto) { return this.prisma.user.create({ data: createUserDto, }); } async findAll() { return this.prisma.user.findMany(); } async findOne(id: number) { return this.prisma.user.findUnique({ where: { id }, }); } async update(id: number, updateUserDto: any) { return this.prisma.user.update({ where: { id }, data: updateUserDto, }); } async remove(id: number) { return this.prisma.user.delete({ where: { id }, }); } }
Mongoose Integration
Install Dependencies
bashnpm install @nestjs/mongoose mongoose
Configure Mongoose
typescriptimport { Module } from '@nestjs/common'; import { MongooseModule } from '@nestjs/mongoose'; import { User, UserSchema } from './schemas/user.schema'; import { UserService } from './user.service'; import { UserController } from './user.controller'; @Module({ imports: [ MongooseModule.forRoot('mongodb://localhost:27017/nest'), MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]), ], providers: [UserService], controllers: [UserController], }) export class UserModule {}
Create Schema
typescriptimport { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; import { Document } from 'mongoose'; @Schema() export class User extends Document { @Prop({ required: true, unique: true }) email: string; @Prop({ required: true }) password: string; @Prop({ required: true }) name: string; @Prop({ default: true }) isActive: boolean; @Prop({ default: Date.now }) createdAt: Date; @Prop({ default: Date.now }) updatedAt: Date; } export const UserSchema = SchemaFactory.createForClass(User);
Create Mongoose Service
typescriptimport { Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; import { User } from './schemas/user.schema'; import { CreateUserDto } from './dto/create-user.dto'; @Injectable() export class UserService { constructor( @InjectModel(User.name) private userModel: Model<User>, ) {} async create(createUserDto: CreateUserDto): Promise<User> { const createdUser = new this.userModel(createUserDto); return createdUser.save(); } async findAll(): Promise<User[]> { return this.userModel.find().exec(); } async findOne(id: string): Promise<User> { return this.userModel.findOne({ _id: id }).exec(); } async update(id: string, updateUserDto: any): Promise<User> { return this.userModel.findByIdAndUpdate(id, updateUserDto, { new: true }).exec(); } async remove(id: string): Promise<User> { return this.userModel.findByIdAndDelete(id).exec(); } }
Database Migrations
TypeORM Migrations
bash# Generate migration npm run typeorm migration:generate -- -n CreateUserMigration # Run migration npm run typeorm migration:run # Revert migration npm run typeorm migration:revert
Prisma Migrations
bash# Create migration npx prisma migrate dev --name init # Apply migration npx prisma migrate deploy # Reset database npx prisma migrate reset
Transaction Handling
TypeORM Transactions
typescriptimport { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository, DataSource } from 'typeorm'; import { User } from './entities/user.entity'; import { Order } from './entities/order.entity'; @Injectable() export class OrderService { constructor( private dataSource: DataSource, @InjectRepository(User) private userRepository: Repository<User>, @InjectRepository(Order) private orderRepository: Repository<Order>, ) {} async createOrderWithUser(userId: number, orderData: any) { const queryRunner = this.dataSource.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); try { const user = await queryRunner.manager.findOne(User, { where: { id: userId } }); const order = queryRunner.manager.create(Order, { ...orderData, user, }); await queryRunner.manager.save(order); await queryRunner.commitTransaction(); return order; } catch (err) { await queryRunner.rollbackTransaction(); throw err; } finally { await queryRunner.release(); } } }
Prisma Transactions
typescriptasync createOrderWithUser(userId: number, orderData: any) { return this.prisma.$transaction(async (prisma) => { const user = await prisma.user.findUnique({ where: { id: userId }, }); const order = await prisma.order.create({ data: { ...orderData, user: { connect: { id: userId }, }, }, }); return order; }); }
Relationship Mapping
TypeORM Relationships
typescript@Entity('users') export class User { @PrimaryGeneratedColumn() id: number; @Column() name: string; @OneToMany(() => Order, order => order.user) orders: Order[]; } @Entity('orders') export class Order { @PrimaryGeneratedColumn() id: number; @Column() userId: number; @ManyToOne(() => User, user => user.orders) user: User; @Column() product: string; }
Prisma Relationships
prismamodel User { id Int @id @default(autoincrement()) name String orders Order[] } model Order { id Int @id @default(autoincrement()) userId Int user User @relation(fields: [userId], references: [id]) product String }
Database Connection Pool Configuration
TypeORM Connection Pool
typescriptTypeOrmModule.forRoot({ type: 'mysql', host: 'localhost', port: 3306, username: 'root', password: 'password', database: 'test', entities: [__dirname + '/**/*.entity{.ts,.js}'], synchronize: false, extra: { connectionLimit: 10, }, })
Prisma Connection Pool
prismadatasource db { provider = "mysql" url = env("DATABASE_URL") // Connection pool configuration connection_limit = 10 }
Best Practices
- Use DTOs: Use data transfer objects to validate and transform data
- Environment Variables: Use environment variables to manage database configuration
- Migration Management: Use migrations to manage database schema changes
- Transaction Handling: Use transactions when atomic operations are needed
- Index Optimization: Add indexes for frequently queried fields
- Connection Pooling: Configure connection pool size appropriately
- Soft Delete: Implement soft delete instead of physical delete
- Query Optimization: Avoid N+1 query problems
Summary
NestJS database integration provides:
- Support for multiple databases
- Flexible ORM/ODM choices
- Complete type safety
- Powerful relationship mapping
- Convenient transaction handling
Mastering database integration is the foundation for building data-driven NestJS applications. By properly selecting and using ORM/ODM and following best practices, you can build high-performance, maintainable data access layers.