Trong kiến trúc NestJS kết hợp với TypeORM, Repository là lớp trung gian giúp bạn thực hiện các thao tác đọc ghi dữ liệu xuống cơ sở dữ liệu mà không cần phải viết các câu lệnh SQL thuần túy. Tài liệu này tổng hợp toàn bộ các phương thức (methods) quan trọng nhất của Repository lớp trong TypeORM, phân loại mục đích sử dụng và hướng dẫn cách ra lệnh cho Claude Code lựa chọn phương thức tối ưu nhất cho từng bài toán cụ thể.

Phân loại các Phương thức trong Repository

TypeORM chia các phương thức tương tác dữ liệu thành 4 nhóm chính: Khởi tạo, Truy vấn, Cập nhật và Xóa.

1. Nhóm Phương thức Khởi tạo (Creation)

Các phương thức giúp tạo mới thực thể trong ứng dụng trước khi lưu xuống cơ sở dữ liệu.

create()

  • Ý nghĩa: Khởi tạo một đối tượng thực thể (instance) mới từ dữ liệu truyền vào.
  • Đặc điểm: Không thực hiện bất kỳ câu lệnh SQL nào và không lưu vào Database. Nó chỉ đơn thuần là tạo ra một đối tượng Javascript có kiểu là lớp thực thể đó để chuẩn bị lưu.
  • Cú pháp:
    const newProduct = this.productRepository.create({ name: 'Bàn phím', price: 500000 });
    

2. Nhóm Phương thức Truy vấn (Finding)

Giúp đọc dữ liệu từ Database lên bộ nhớ ứng dụng.
Tên Phương thứcÝ nghĩaVí dụ thực tế
find()Lấy danh sách các bản ghi thỏa mãn điều kiện lọc, sắp xếp, phân trang.find({ where: { isAvailable: true }, order: { price: 'ASC' } })
findBy()Phiên bản đơn giản hơn của find(), chỉ lọc nhanh bằng các thuộc tính trực tiếp.findBy({ isAvailable: true })
findOne()Lấy bản ghi đầu tiên thỏa mãn điều kiện. Trả về null nếu không thấy.findOne({ where: { id: 1 } })
findOneBy()Phiên bản đơn giản hơn của findOne(), tìm nhanh theo thuộc tính trực tiếp.findOneBy({ id: 1 })
findOneOrFail()Tìm bản ghi đầu tiên, nếu không thấy sẽ tự động ném lỗi (EntityNotFoundError).findOneOrFail({ where: { sku: 'P001' } })
count()Đếm tổng số bản ghi thỏa mãn điều kiện.count({ where: { isAvailable: false } })

Ví dụ truy vấn nâng cao với điều kiện và nạp quan hệ (Relations):

const activeUsers = await this.userRepository.find({
  select: ['id', 'email'], // Chỉ lấy cột id và email
  where: { isActive: true },
  relations: ['profile', 'orders'], // Nạp kèm bảng liên kết (Join)
  take: 10, // Giới hạn 10 bản ghi (Limit)
  skip: 0, // Bỏ qua 0 bản ghi (Offset - dùng cho phân trang)
});

3. Nhóm Phương thức Ghi & Cập nhật (Saving & Updating)

Có hai trường phái cập nhật dữ liệu trong TypeORM với các ưu nhược điểm riêng biệt mà lập trình viên cần phân biệt.
save() được dùng cho cả việc tạo mới (INSERT) lẫn cập nhật (UPDATE). Nếu đối tượng truyền vào chưa có khóa chính, TypeORM sẽ tạo mới. Nếu đã có khóa chính, nó sẽ cập nhật bản ghi tương ứng.
  • Quy trình cập nhật:
    1. Tìm thực thể cũ bằng findOne().
    2. Dùng merge() để gộp các thay đổi từ DTO vào thực thể cũ.
    3. Chạy save() để lưu.
  • Ưu điểm: Tự động kích hoạt toàn bộ các hooks của thực thể (như @BeforeUpdate(), @AfterUpdate()), kiểm soát dữ liệu chính xác.
  • Ví dụ:
    const product = await this.productRepository.findOneBy({ id });
    const updatedProduct = this.productRepository.merge(product, updateProductDto);
    await this.productRepository.save(updatedProduct);
    

4. Nhóm Phương thức Xóa (Deleting & Removing)

Tương tự như cập nhật, việc xóa cũng chia làm hai trường phái dựa trên hiệu năng và tính năng kích hoạt hooks.

delete() vs remove()

  • delete(): Chạy trực tiếp lệnh DELETE SQL trong DB dựa trên ID hoặc điều kiện truyền vào. Hiệu năng cực cao, không kích hoạt thực thể hooks.
    await this.productRepository.delete(1); // Xóa sản phẩm ID #1 trực tiếp
    await this.productRepository.delete({ isAvailable: false }); // Xóa nhiều
    
  • remove(): Nhận vào một thực thể cụ thể đã tải lên bộ nhớ và xóa thực thể đó. Kích hoạt toàn bộ thực thể hooks (như @BeforeRemove()), tốn tài nguyên hơn.
    const product = await this.productRepository.findOneBy({ id: 1 });
    await this.productRepository.remove(product);
    

Xóa mềm (Soft Delete)

Đối với dữ liệu quan trọng, thay vì xóa vật lý khỏi ổ cứng, ta thường đánh dấu xóa mềm bằng cách điền ngày xóa vào cột @DeleteDateColumn().
  • softDelete(): Xóa mềm trực tiếp bằng ID không cần nạp thực thể.
  • softRemove(): Xóa mềm thực thể đã nạp trên bộ nhớ (chạy hooks).
  • restore(): Khôi phục lại bản ghi đã bị xóa mềm trước đó.
    await this.productRepository.softDelete(1); // Xóa mềm
    await this.productRepository.restore(1); // Khôi phục lại dữ liệu
    

Hướng dẫn viết Prompt tối ưu hóa Repository với Claude Code

Khi giao tiếp với Claude Code, bạn nên nêu rõ yêu cầu về hiệu năng hoặc nghiệp vụ để AI chọn phương thức tối ưu nhất.

1. Prompt yêu cầu nghiệp vụ chuẩn (Cần chạy hooks/subscribers):

Hãy viết hàm update danh mục (Category) trong CategoryService sử dụng TypeORM.
Yêu cầu: Tôi có các hook @BeforeUpdate() trong entity để tự sinh slug từ danh mục cha. 
Hãy chọn phương thức cập nhật phù hợp (merge + save) để các hook này hoạt động chính xác.

2. Prompt yêu cầu hiệu năng cao (Không cần hooks):

Hãy viết hàm vô hiệu hóa hàng loạt tài khoản người dùng không hoạt động (status = 'inactive') trong UserService.
Yêu cầu: Số lượng tài khoản rất lớn, hãy sử dụng phương thức update trực tiếp của Repository để chạy một câu lệnh SQL duy nhất xuống PostgreSQL, tránh tải hàng loạt thực thể lên bộ nhớ gây nghẽn RAM.