0
0
Lập trình
TT

Hướng dẫn sử dụng TypeORM với NestJS và PostgreSQL

Đăng vào 1 tháng trước

• 5 phút đọc

Hướng dẫn sử dụng TypeORM với NestJS và PostgreSQL

Mục lục

  1. Giới thiệu
  2. Cài đặt và thiết lập
  3. Định nghĩa Entities
  4. Repositories và Services
  5. Ví dụ về Controller
  6. Ví dụ truy vấn
  7. Quan hệ trong TypeORM
  8. Giao dịch
  9. Di chuyển và nâng cấp
  10. Chỉ số và ràng buộc
  11. Mẹo nâng cao
  12. Thực tiễn tốt nhất
  13. Lỗi thường gặp
  14. Câu hỏi thường gặp

Giới thiệu

TypeORM là một ORM (Object-Relational Mapping) mạnh mẽ cho TypeScript và JavaScript, giúp bạn dễ dàng tương tác với cơ sở dữ liệu PostgreSQL trong các ứng dụng NestJS. Bài viết này sẽ hướng dẫn bạn từ cài đặt đến việc sử dụng TypeORM một cách hiệu quả.

Cài đặt và thiết lập

Để bắt đầu, trước tiên bạn cần cài đặt các gói cần thiết. Bạn có thể thực hiện lệnh sau:

bash Copy
npm install --save @nestjs/typeorm typeorm pg

Sau khi cài đặt xong, bạn cần cấu hình TypeORM trong module chính của ứng dụng.

typescript Copy
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './users/user.entity';
import { UsersModule } from './users/users.module';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'postgres',
      host: 'localhost',
      port: 5432,
      username: 'postgres',
      password: 'yourpassword',
      database: 'testdb',
      entities: [User],
      synchronize: true,
    }),
    UsersModule,
  ],
})
export class AppModule {}

Định nghĩa Entities

Entities là các lớp đại diện cho bảng trong cơ sở dữ liệu. Dưới đây là ví dụ về cách định nghĩa một entity cho người dùng và bài viết:

User Entity

typescript Copy
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, OneToMany } from 'typeorm';
import { Post } from './post.entity';

@Entity()
export class User {
  @PrimaryGeneratedColumn() id: number;
  @Column({ unique: true }) email: string;
  @Column() name: string;
  @Column({ nullable: true }) age: number;
  @CreateDateColumn() createdAt: Date;
  @UpdateDateColumn() updatedAt: Date;
  @OneToMany(() => Post, post => post.user)
  posts: Post[];
}

Post Entity

typescript Copy
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.entity';

@Entity()
export class Post {
  @PrimaryGeneratedColumn() id: number;
  @Column() title: string;
  @Column({ type: 'text' }) content: string;
  @ManyToOne(() => User, user => user.posts)
  user: User;
}

Repositories và Services

Repositories cung cấp các phương thức để tương tác với cơ sở dữ liệu. Dưới đây là ví dụ về một service cho người dùng:

typescript Copy
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';

@Injectable()
export class UsersService {
  constructor(@InjectRepository(User) private usersRepo: Repository<User>) {}

  findAll() { return this.usersRepo.find({ relations: ['posts'] }); }
  findOne(id: number) { return this.usersRepo.findOne({ where: { id }, relations: ['posts'] }); }
  create(userData: Partial<User>) { const user = this.usersRepo.create(userData); return this.usersRepo.save(user); }
  update(id: number, userData: Partial<User>) { return this.usersRepo.update(id, userData); }
  remove(id: number) { return this.usersRepo.delete(id); }
}

Ví dụ về Controller

Controller nhận yêu cầu từ client và trả về phản hồi. Dưới đây là ví dụ về controller cho người dùng:

typescript Copy
import { Controller, Get, Post, Body, Param, Put, Delete } from '@nestjs/common';
import { UsersService } from './users.service';
import { User } from './user.entity';

@Controller('users')
export class UsersController {
  constructor(private usersService: UsersService) {}

  @Get() getAll() { return this.usersService.findAll(); }
  @Get(':id') getOne(@Param('id') id: number) { return this.usersService.findOne(id); }
  @Post() create(@Body() userData: Partial<User>) { return this.usersService.create(userData); }
  @Put(':id') update(@Param('id') id: number, @Body() userData: Partial<User>) { return this.usersService.update(id, userData); }
  @Delete(':id') remove(@Param('id') id: number) { return this.usersService.remove(id); }
}

Ví dụ truy vấn

TypeORM hỗ trợ nhiều cách để thực hiện truy vấn. Dưới đây là một số ví dụ:

Sử dụng QueryBuilder

typescript Copy
const users = await this.usersRepo.createQueryBuilder('user')
  .leftJoinAndSelect('user.posts', 'post')
  .where('user.age > :age', { age: 25 })
  .orderBy('user.name', 'ASC')
  .getMany();

Sử dụng SQL thuần

typescript Copy
const result = await this.usersRepo.query('SELECT * FROM "user" WHERE age > $1', [25]);

Quan hệ trong TypeORM

TypeORM hỗ trợ nhiều loại quan hệ giữa các bảng:

  • OneToOne: User → Profile
  • OneToMany / ManyToOne: User → Posts
  • ManyToMany: Posts ↔ Tags

Ví dụ về quan hệ ManyToMany:

typescript Copy
@ManyToMany(() => Tag, tag => tag.posts)
@JoinTable()
tags: Tag[];

Giao dịch

Giao dịch giúp đảm bảo tính toàn vẹn dữ liệu khi thực hiện nhiều thao tác trong cơ sở dữ liệu. Dưới đây là ví dụ về cách sử dụng giao dịch trong TypeORM:

typescript Copy
await this.dataSource.transaction(async (manager) => {
  await manager.update(User, { id: 1 }, { age: 30 });
  await manager.update(Post, { id: 5 }, { title: 'Updated' });
});

Di chuyển và nâng cấp

Để quản lý các thay đổi trong cấu trúc cơ sở dữ liệu, bạn có thể sử dụng migrations. Dưới đây là các lệnh để tạo và chạy migrations:

bash Copy
npx typeorm migration:generate -n CreateUsers
npx typeorm migration:run
npx typeorm migration:revert

Chỉ số và ràng buộc

Chỉ số và ràng buộc giúp tối ưu hóa hiệu suất truy vấn. Ví dụ:

typescript Copy
@Column({ unique: true }) email: string;
@Column({ nullable: true }) username: string;
@Index('idx_user_email')
@Column() email: string;

Mẹo nâng cao

  1. Sử dụng QueryBuilder cho các truy vấn phức tạp với joins, phân trang và lọc.
  2. Tránh vấn đề N+1 bằng cách sử dụng leftJoinAndSelect.
  3. Sử dụng giao dịch cho các thao tác nhiều bảng.
  4. Sử dụng migrations trong môi trường sản xuất, không sử dụng synchronize: true.
  5. Kết hợp với Redis caching cho các truy vấn nặng.
  6. Sử dụng DTOs + ValidationPipe để xác thực đầu vào.

Thực tiễn tốt nhất

  • Luôn xác định rõ ràng các entity và quan hệ giữa chúng.
  • Sử dụng các phương thức của repository một cách hiệu quả.
  • Chú ý tới hiệu suất khi thực hiện các truy vấn lớn.

Lỗi thường gặp

  • Lỗi kết nối tới cơ sở dữ liệu: Kiểm tra thông tin kết nối trong cấu hình.
  • Lỗi không tìm thấy entity: Đảm bảo entity đã được khai báo trong module.

Câu hỏi thường gặp

TypeORM là gì?

TypeORM là một ORM cho TypeScript và JavaScript, giúp tương tác với cơ sở dữ liệu dễ dàng hơn.

Làm thế nào để sử dụng TypeORM với NestJS?

Cài đặt gói TypeORM, thiết lập cấu hình trong module và định nghĩa entities.

Có cần sử dụng migrations không?

Có, migrations giúp quản lý các thay đổi trong cơ sở dữ liệu một cách an toàn và hiệu quả.

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào