blog.dopana

Back

Prisma là ORM hiện đại cho TypeScript. Không như TypeORM hay Sequelize — Prisma tự động sinh type, giúp DevX cực kỳ tốt.

Setup#

npm install prisma @prisma/client
npx prisma init
bash

Tạo file prisma/schema.prisma:

Migrations#

# Tạo migration
npx prisma migrate dev --name init

# Áp dụng migration lên production
npx prisma migrate deploy

# Reset database
npx prisma migrate reset

# Xem migration history
npx prisma migrate status
bash

CRUD Cơ Bản#

Relations#

Include — Load Relation#

const user = await prisma.user.findUnique({
  where: { id: 1 },
  include: {
    posts: {
      where: { published: true },
      orderBy: { createdAt: 'desc' },
      take: 5,
    },
  },
});
typescript

Nested Create#

const user = await prisma.user.create({
  data: {
    email: 'bob@example.com',
    name: 'Bob',
    posts: {
      create: [
        { title: 'Post 1', content: 'Hello' },
        { title: 'Post 2', content: 'World' },
      ],
    },
  },
  include: { posts: true },
});
typescript

Nested Update#

await prisma.user.update({
  where: { id: 1 },
  data: {
    posts: {
      update: {
        where: { id: 10 },
        data: { title: 'Updated Title' },
      },
      create: { title: 'New Post', content: 'Content' },
      delete: { id: 5 },
    },
  },
});
typescript

Transactions#

// Interactive transaction
await prisma.$transaction(async (tx) => {
  const user = await tx.user.create({ data: { email, name } });
  const profile = await tx.profile.create({
    data: { userId: user.id, bio },
  });
  return { user, profile };
});

// Batch transaction
const [user1, user2] = await prisma.$transaction([
  prisma.user.create({ data: { email: 'a@test.com', name: 'A' } }),
  prisma.user.create({ data: { email: 'b@test.com', name: 'B' } }),
]);
typescript

Pagination#

// Offset pagination
const posts = await prisma.post.findMany({
  skip: (page - 1) * limit,
  take: limit,
  orderBy: { createdAt: 'desc' },
});

// Cursor pagination (nhanh hơn cho large dataset)
const posts = await prisma.post.findMany({
  take: limit + 1, // Lấy dư 1 để biết có next page
  cursor: cursor ? { id: cursor } : undefined,
  orderBy: { id: 'asc' },
  where: { published: true },
});
typescript

Aggregation#

const stats = await prisma.post.aggregate({
  _count: { id: true },
  _avg: { viewCount: true },
  _max: { viewCount: true },
  where: { published: true },
});

const group = await prisma.post.groupBy({
  by: ['authorId'],
  _count: { id: true },
  _sum: { viewCount: true },
  orderBy: { _sum: { viewCount: 'desc' } },
});
typescript

Middleware — Hook#

Raw Query#

// Khi Prisma query không đủ
const users = await prisma.$queryRaw`
  SELECT id, name, COUNT(p.id) as post_count
  FROM users u
  LEFT JOIN posts p ON p.author_id = u.id
  GROUP BY u.id
  HAVING COUNT(p.id) > 5
`;

// Execute raw
await prisma.$executeRaw`UPDATE posts SET view_count = view_count + 1 WHERE id = ${postId}`;
typescript

Best Practices#

Migration Production#

# Không dùng migrate dev trên production
# Dùng migrate deploy — chỉ chạy migration mới
npx prisma migrate deploy

# Nếu cần chỉnh sửa migration đã áp dụng
npx prisma migrate resolve --applied 20240718000000_init
bash

Kết Luận#

Prisma giúp làm việc với database dễ hơn rất nhiều:

  • Không viết SQL (trừ khi cần)
  • Type-safe từ đầu đến cuối
  • Migration tự động
  • Relations xử lý dễ dàng
  • DevX xuất sắc

Nhớ: Luôn dùng select để tránh over-fetching. Transaction cho multi-step operations. Migration deploy cho production.

Tài liệu tham khảo#