Skip to main content

Lesson 5: Reference & Error Handling

📋 Agenda

Thời gian đọc ước tính: ~12 phút

Sau bài này, bạn sẽ:

  • Tra cứu nhanh các lệnh CLI Prisma hay dùng nhất
  • Cấu hình đúng Connection URL cho PostgreSQL, MySQL, MongoDB
  • Bắt lỗi Prisma đúng cách với PrismaClientKnownRequestError
  • Xử lý các mã lỗi phổ biến: P2002 (unique conflict) và P2025 (not found)

Yêu cầu đầu vào (Prerequisites):

  • 🔹 Đã đọc qua Lesson 1-4 hoặc có kinh nghiệm với Prisma cơ bản
  • 🔹 Hiểu try-catch và error handling trong TypeScript

🔨 Prisma CLI Reference — Cheat Sheet

Các lệnh hay dùng nhất

LệnhMô tả
prisma initKhởi tạo Prisma trong project
prisma generateSinh/cập nhật Prisma Client từ schema
prisma db pullIntrospect DB → ghi vào schema.prisma
prisma db pushPush schema lên DB (không tạo migration file)
prisma migrate devTạo & apply migration cho development
prisma migrate deployApply migration pending cho production
prisma migrate resetXóa DB và apply lại toàn bộ migration (dev only)
prisma migrate statusKiểm tra trạng thái migration
prisma migrate diffXem diff SQL giữa schema và DB
prisma studioMở GUI quản lý data tại http://localhost:5555
prisma formatFormat file schema.prisma đẹp
prisma validateKiểm tra cú pháp schema.prisma

Lệnh với options quan trọng

# migrate dev — tạo migration với tên mô tả rõ ràng
npx prisma migrate dev --name "add_user_profile_table"

# migrate dev — skip generate (nếu muốn manual)
npx prisma migrate dev --skip-generate

# migrate deploy — dùng trong CI, không prompt
npx prisma migrate deploy

# db push — nhanh trong prototyping, KHÔNG dùng production
npx prisma db push --force-reset # Xóa sạch DB và push lại

# generate — chỉ định schema khác nếu không dùng default
npx prisma generate --schema=./custom/schema.prisma

# migrate diff — preview SQL changes
npx prisma migrate diff \
--from-schema-datasource prisma/schema.prisma \
--to-schema-datamodel prisma/schema.prisma \
--script

# studio — đổi port nếu 5555 đang dùng
npx prisma studio --port 5556

🔨 Connection URLs & Environment Variables

Cấu trúc Connection URL

protocol://USER:PASSWORD@HOST:PORT/DATABASE?param1=value&param2=value

PostgreSQL

# filename: .env

# Local development
DATABASE_URL="postgresql://postgres:secret@localhost:5432/mydb"

# Với SSL (Production)
DATABASE_URL="postgresql://user:pass@prod-host:5432/mydb?sslmode=require"

# Connection pooling với PgBouncer
DATABASE_URL="postgresql://user:pass@pgbouncer:6432/mydb?pgbouncer=true&connection_limit=1"

# Prisma Accelerate
DATABASE_URL="prisma://accelerate.prisma-data.net/?api_key=YOUR_API_KEY"

MySQL / MariaDB

# MySQL
DATABASE_URL="mysql://user:pass@localhost:3306/mydb"

# Với SSL
DATABASE_URL="mysql://user:pass@prod-host:3306/mydb?sslaccept=strict"

# PlanetScale (serverless MySQL)
DATABASE_URL="mysql://user:pass@host.psdb.cloud/mydb?sslaccept=strict"

MongoDB

# MongoDB Atlas
DATABASE_URL="mongodb+srv://user:pass@cluster.mongodb.net/mydb?retryWrites=true"

# Local MongoDB
DATABASE_URL="mongodb://localhost:27017/mydb"

SQLite (cho development/testing)

# File-based — tiện cho unit test local
DATABASE_URL="file:./dev.db"

# In-memory (xóa khi app tắt)
DATABASE_URL="file::memory:?cache=shared"

Quản lý Environment Variables

# filename: .env (Development — KHÔNG commit lên Git)
DATABASE_URL="postgresql://postgres:dev-secret@localhost:5432/myapp_dev"
SHADOW_DATABASE_URL="postgresql://postgres:dev-secret@localhost:5432/myapp_shadow"

# filename: .env.example (Template — ĐượC commit, không chứa secret)
DATABASE_URL="postgresql://USER:PASSWORD@HOST:PORT/DATABASE"
// Validate env vars khi startup — phát hiện lỗi config sớm
if (!process.env.DATABASE_URL) {
throw new Error('DATABASE_URL is required but not set in environment');
}

🔨 Prisma API Reference — Tóm tắt cú pháp

Schema API — Các attribute cần nhớ

model User {
// --- ID & Default ---
id Int @id @default(autoincrement())
uuid String @id @default(uuid())
cuid String @id @default(cuid())

// --- Type modifiers ---
name String // NOT NULL
bio String? // NULL (optional)

// --- Constraints ---
email String @unique
slug String @db.VarChar(100) // DB-level type

// --- Timestamps ---
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

// --- Table-level ---
@@unique([firstName, lastName]) // Composite unique
@@index([email, createdAt]) // Composite index
@@map("users") // Custom table name
}

Prisma Client API — Quick Reference

// --- READ ---
prisma.model.findUnique({ where: { id } })
prisma.model.findFirst({ where, orderBy, include })
prisma.model.findMany({ where, orderBy, take, skip, cursor, include, select })
prisma.model.count({ where })
prisma.model.aggregate({ where, _avg, _sum, _min, _max, _count })
prisma.model.groupBy({ by, where, _count, orderBy, having })

// --- WRITE ---
prisma.model.create({ data, select, include })
prisma.model.createMany({ data: [{}], skipDuplicates })
prisma.model.update({ where, data, select })
prisma.model.updateMany({ where, data })
prisma.model.upsert({ where, create, update })
prisma.model.delete({ where })
prisma.model.deleteMany({ where })

// --- TRANSACTION ---
await prisma.$transaction([
prisma.model.create({ data: {} }),
prisma.model.update({ where: {}, data: {} }),
]);

// Interactive transaction
await prisma.$transaction(async (tx) => {
const a = await tx.account.update({ where: { id: 1 }, data: { balance: { decrement: 100 } } });
const b = await tx.account.update({ where: { id: 2 }, data: { balance: { increment: 100 } } });
if (a.balance < 0) throw new Error('Insufficient funds'); // Rollback cả transaction
return [a, b];
});

🔨 Error Handling — Bắt lỗi đúng cách

PrismaClientKnownRequestError

// filename: src/utils/prisma-error.handler.ts

import { Prisma } from '@prisma/client';

// Template xử lý lỗi Prisma chuẩn
export function handlePrismaError(error: unknown): never {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
// Lỗi từ database với mã cụ thể (P2xxx)
switch (error.code) {
case 'P2002':
// Unique constraint violation
const field = (error.meta?.target as string[])?.join(', ');
throw new ConflictException(`Trường ${field} đã tồn tại trong hệ thống`);

case 'P2025':
// Record không tồn tại
throw new NotFoundException(`Không tìm thấy dữ liệu yêu cầu`);

case 'P2003':
// Foreign key constraint failed
throw new BadRequestException(
`Dữ liệu liên kết không hợp lệ: ${error.meta?.field_name}`
);

case 'P2014':
// Required relation violation
throw new BadRequestException('Vi phạm ràng buộc quan hệ bắt buộc');

default:
// Log lỗi chưa biết để debug
console.error(`Unhandled Prisma error code: ${error.code}`, error);
throw new InternalServerErrorException('Lỗi cơ sở dữ liệu');
}
}

if (error instanceof Prisma.PrismaClientValidationError) {
// Lỗi validation: query sai cú pháp, thiếu field required
throw new BadRequestException(`Dữ liệu query không hợp lệ: ${error.message}`);
}

if (error instanceof Prisma.PrismaClientInitializationError) {
// Không kết nối được database
throw new ServiceUnavailableException('Không thể kết nối database');
}

throw error; // Re-throw nếu không phải Prisma error
}

Sử dụng trong Service

// filename: src/services/user.service.ts

import { prisma } from '../lib/prisma';
import { handlePrismaError } from '../utils/prisma-error.handler';

export class UserService {
async createUser(email: string, name: string) {
try {
return await prisma.user.create({
data: { email, name },
});
} catch (error) {
handlePrismaError(error); // Chuyển đổi Prisma error → HTTP error
}
}

async getUserById(id: number) {
// Cách 1: Dùng findUniqueOrThrow — tự throw P2025 nếu không tìm thấy
return prisma.user.findUniqueOrThrow({
where: { id },
});

// Cách 2: Manual check
// const user = await prisma.user.findUnique({ where: { id } });
// if (!user) throw new NotFoundException(`User #${id} not found`);
// return user;
}
}

Bảng mã lỗi phổ biến

Mã lỗiTênKhi nào xảy raCách xử lý
P2002UniqueConstraintViolationInsert/Update vi phạm @uniqueTrả 409 Conflict, thông báo field trùng
P2003ForeignKeyConstraintViolationFK không tồn tại trong bảng chaTrả 400 Bad Request
P2014RequiredRelationViolationVi phạm required relationTrả 400 Bad Request
P2015RelatedRecordNotFoundRecord liên kết không tồn tạiTrả 404 Not Found
P2025RecordNotFoundfindUniqueOrThrow/findFirstOrThrow không có recordTrả 404 Not Found
P2034TransactionConflictDeadlock hoặc write conflictRetry transaction

🔨 Database Features & System Requirements

Tính năng theo Database

FeaturePostgreSQLMySQLSQLiteMongoDB
Migrations
Transactions✅ (4.0+)
Full-text Search
JSON fields✅ (8.0+)✅ (Native)
Extensions✅ (pgcrypto...)
Array types

System Requirements

Node.js: >= 16.13 (LTS khuyến nghị >= 18)
TypeScript: >= 4.7
Prisma: >= 5.0 (latest stable)

🗺️ MECE Mindmap


Made by Anh Tu - Share to be share