This article discusses in depth how to integrate TypeORM in NestJS for database operations, including TypeORM configuration and integration, entity design and relationship mapping, application of Repository mode, transaction processing scheme, database migration management, and performance optimization strategies.
TypeORM Integrated Configuration
1. Installation dependencies
First install the necessary dependency packages:
npm install @nestjs/typeorm typeorm pg # If using MySQL# npm install @nestjs/typeorm typeorm mysql2
2. Database configuration
// src/config/ import { TypeOrmModuleOptions } from '@nestjs/typeorm'; export const databaseConfig: TypeOrmModuleOptions = { type: 'postgres', host: .DB_HOST || 'localhost', port: parseInt(.DB_PORT) || 5432, username: .DB_USERNAME || 'postgres', password: .DB_PASSWORD || 'postgres', database: .DB_DATABASE || 'nestjs_db', entities: ['dist/**/*.entity{.ts,.js}'], synchronize: .NODE_ENV !== 'production', logging: .NODE_ENV !== 'production', ssl: .DB_SSL === 'true', }; // src/ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { databaseConfig } from './config/'; @Module({ imports: [ (databaseConfig), // Other modules ], }) export class AppModule {}
Entity Design and Relationship Mapping
1. Basic physical design
// src/entities/ import { PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn, DeleteDateColumn } from 'typeorm'; export abstract class BaseEntity { @PrimaryGeneratedColumn('uuid') id: string; @CreateDateColumn() createdAt: Date; @UpdateDateColumn() updatedAt: Date; @DeleteDateColumn() deletedAt: Date; } // src/users/entities/ import { Entity, Column, OneToMany } from 'typeorm'; import { BaseEntity } from '../entities/'; import { Post } from './'; @Entity('users') export class User extends BaseEntity { @Column({ length: 100 }) name: string; @Column({ unique: true }) email: string; @Column({ select: false }) password: string; @OneToMany(() => Post, post => ) posts: Post[]; } // src/posts/entities/ import { Entity, Column, ManyToOne, JoinColumn } from 'typeorm'; import { BaseEntity } from '../entities/'; import { User } from './'; @Entity('posts') export class Post extends BaseEntity { @Column() title: string; @Column('text') content: string; @Column({ default: false }) published: boolean; @ManyToOne(() => User, user => ) @JoinColumn({ name: 'author_id' }) author: User; }
2. Relationship Mapping Strategy
// src/users/entities/ import { Entity, Column, OneToOne, JoinColumn } from 'typeorm'; import { BaseEntity } from '../entities/'; import { User } from './'; @Entity('profiles') export class Profile extends BaseEntity { @Column() avatar: string; @Column('text') bio: string; @OneToOne(() => User) @JoinColumn({ name: 'user_id' }) user: User; } // src/posts/entities/ import { Entity, Column, ManyToMany } from 'typeorm'; import { BaseEntity } from '../entities/'; import { Post } from './'; @Entity('tags') export class Tag extends BaseEntity { @Column({ unique: true }) name: string; @ManyToMany(() => Post, post => ) posts: Post[]; } // Update Post entity and add tag relationships@Entity('posts') export class Post extends BaseEntity { // ... Other fields @ManyToMany(() => Tag, tag => ) @JoinTable({ name: 'posts_tags', joinColumn: { name: 'post_id' }, inverseJoinColumn: { name: 'tag_id' } }) tags: Tag[]; }
Database operation implementation
1. Repository mode
// src/users/ import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { User } from './entities/'; import { CreateUserDto, UpdateUserDto } from './dto'; @Injectable() export class UsersService { constructor( @InjectRepository(User) private usersRepository: Repository<User> ) {} async create(createUserDto: CreateUserDto): Promise<User> { const user = (createUserDto); return await (user); } async findAll(): Promise<User[]> { return await ({ relations: ['posts', 'profile'] }); } async findOne(id: string): Promise<User> { const user = await ({ where: { id }, relations: ['posts', 'profile'] }); if (!user) { throw new NotFoundException(`User with ID ${id} not found`); } return user; } async update(id: string, updateUserDto: UpdateUserDto): Promise<User> { const user = await (id); (user, updateUserDto); return await (user); } async remove(id: string): Promise<void> { const user = await (id); await (user); } }
2. Query builder
// src/posts/ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { Post } from './entities/'; @Injectable() export class PostsService { constructor( @InjectRepository(Post) private postsRepository: Repository<Post> ) {} async findPublishedPosts() { return await .createQueryBuilder('post') .leftJoinAndSelect('', 'author') .leftJoinAndSelect('', 'tags') .where(' = :published', { published: true }) .orderBy('', 'DESC') .getMany(); } async searchPosts(query: string) { return await .createQueryBuilder('post') .leftJoinAndSelect('', 'author') .where(' ILIKE :query OR ILIKE :query', { query: `%${query}%` }) .orderBy('', 'DESC') .getMany(); } async getPostStats() { return await .createQueryBuilder('post') .select('', 'authorName') .addSelect('COUNT(*)', 'postCount') .leftJoin('', 'author') .groupBy('') .getRawMany(); } }
Transaction processing
1. Transaction Decorator
// src/common/decorators/ import { createParamDecorator, ExecutionContext } from '@nestjs/common'; import { getManager } from 'typeorm'; export const Transaction = createParamDecorator( async (data: unknown, ctx: ExecutionContext) => { const queryRunner = getManager().(); await (); await (); return queryRunner; } ); // Use example@Post('transfer') async transfer( @Transaction() queryRunner, @Body() transferDto: TransferDto ) { try { // Perform the transfer operation await (Account, , { balance: () => `balance - ${}` } ); await (Account, , { balance: () => `balance + ${}` } ); await (); } catch (err) { await (); throw err; } finally { await (); } }
2. Transaction Manager
// src/common/services/ import { Injectable } from '@nestjs/common'; import { Connection, QueryRunner } from 'typeorm'; @Injectable() export class TransactionService { constructor(private connection: Connection) {} async executeInTransaction<T>( callback: (queryRunner: QueryRunner) => Promise<T> ): Promise<T> { const queryRunner = (); await (); await (); try { const result = await callback(queryRunner); await (); return result; } catch (err) { await (); throw err; } finally { await (); } } } // Use example@Injectable() export class PaymentService { constructor( private transactionService: TransactionService, private ordersService: OrdersService ) {} async processPayment(paymentDto: PaymentDto) { return await (async queryRunner => { const order = await (); // Update order status await (Order, , { status: 'paid' }); // Create a payment record const payment = (Payment, { order, amount: }); await (payment); return payment; }); } }
Database migration
1. Migrate configuration
// = { type: 'postgres', host: .DB_HOST, port: parseInt(.DB_PORT), username: .DB_USERNAME, password: .DB_PASSWORD, database: .DB_DATABASE, entities: ['dist/**/*.entity{.ts,.js}'], migrations: ['dist/migrations/*{.ts,.js}'], cli: { migrationsDir: 'src/migrations' } }; // { "scripts": { "typeorm": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/", "migration:create": "npm run typeorm migration:create -- -n", "migration:generate": "npm run typeorm migration:generate -- -n", "migration:run": "npm run typeorm migration:run", "migration:revert": "npm run typeorm migration:revert" } }
2. Migration example
// src/migrations/ import { MigrationInterface, QueryRunner, Table } from 'typeorm'; export class CreateUsersTable1642340914321 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise<void> { await ( new Table({ name: 'users', columns: [ { name: 'id', type: 'uuid', isPrimary: true, generationStrategy: 'uuid', default: 'uuid_generate_v4()' }, { name: 'name', type: 'varchar', length: '100' }, { name: 'email', type: 'varchar', isUnique: true }, { name: 'password', type: 'varchar' }, { name: 'created_at', type: 'timestamp', default: 'now()' }, { name: 'updated_at', type: 'timestamp', default: 'now()' }, { name: 'deleted_at', type: 'timestamp', isNullable: true } ] }) ); } public async down(queryRunner: QueryRunner): Promise<void> { await ('users'); } }
Performance optimization
1. Query optimization
// src/posts/ @Injectable() export class PostsService { constructor( @InjectRepository(Post) private postsRepository: Repository<Post> ) {} // Use pagination and caching async findAll(page = 1, limit = 10) { const [posts, total] = await ({ relations: ['author', 'tags'], skip: (page - 1) * limit, take: limit, cache: { id: `posts_page_${page}`, milliseconds: 60000 // 1 minute cache } }); return { data: posts, meta: { total, page, lastPage: (total / limit) } }; } // Optimize using subquery async findPopularPosts() { return await .createQueryBuilder('post') .leftJoinAndSelect('', 'author') .addSelect(subQuery => { return subQuery .select('COUNT(*)', 'commentCount') .from('comments', 'comment') .where(' = '); }, 'commentCount') .orderBy('commentCount', 'DESC') .limit(10) .getMany(); } }
2. Index optimization
// src/posts/entities/ @Entity('posts') @Index(['title', 'content']) // Composite indexexport class Post extends BaseEntity { @Column() @Index() // Single column index title: string; @Column('text') content: string; @Column() @Index() authorId: string; // ... Other fields}
Written at the end
This article introduces the database operation practices in NestJS in detail:
- TypeORM configuration and integration
- Entity design and relationship mapping
- Application of Repository Mode
- Transaction Processing Plan
- Database migration management
- Performance optimization strategy
This is the article about integrating TypeORM for database operations in NestJS. For more information about NestJS integrated TypeORM database, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!