
Khi Next.js App Router được giới thiệu, nó đại diện cho một sự thay đổi mô hình cơ bản trong phát triển React full-stack. Bằng cách kết hợp React Server Components (RSC) với Prisma ORM, các nhà phát triển có thể truy vấn cơ sở dữ liệu trực tiếp từ các server component mà không có thác nước tìm nạp phía client, an toàn TypeScript hoàn toàn và các đột biến Server Action dễ dàng.
Trong hướng dẫn toàn diện này, chúng ta sẽ khám phá cách kiến trúc một ứng dụng Next.js và Prisma sẵn sàng cho sản xuất, bao gồm kết nối nhóm (connection pooling), các singleton toàn cục để ngăn chặn cạn kiệt nhóm, Server Actions an toàn kiểu (type-safe), và các script khởi tạo cơ sở dữ liệu tự động.
Cài đặt Prisma và khởi tạo kết nối cơ sở dữ liệu của bạn:
npm install prisma @prisma/client
npx prisma init
Xác định các mô hình quan hệ của bạn bên trong prisma/schema.prisma:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(cuid())
email String @unique
name String?
posts Post[]
createdAt DateTime @default(now())
}
model Post {
id String @id @default(cuid())
title String
content String?
published Boolean @default(false)
authorId String
author User @relation(fields: [authorId], references: [id])
createdAt DateTime @default(now())
}
Trong quá trình phát triển cục bộ, Next.js Fast Refresh xóa bộ nhớ cache mô-đun Node.js trên mỗi lần chỉnh sửa tệp. Nếu bạn khởi tạo new PrismaClient() bên trong các tệp route riêng lẻ, mỗi lần hot reload sẽ tạo các kết nối cơ sở dữ liệu mới, nhanh chóng gây ra lỗi PostgreSQL max_connections.
Tạo một singleton mạnh mẽ trong lib/prisma.ts:
// lib/prisma.ts
import { PrismaClient } from '@prisma/client'
const prismaClientSingleton = () => {
return new PrismaClient({
log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
})
}
declare global {
var prismaGlobal: undefined | ReturnType<typeof prismaClientSingleton>
}
const prisma = globalThis.prismaGlobal ?? prismaClientSingleton()
export default prisma
if (process.env.NODE_ENV !== 'production') globalThis.prismaGlobal = prisma
Vì Server Components chỉ thực thi trên máy chủ, bạn có thể truy vấn Prisma trực tiếp mà không cần tiết lộ chuỗi kết nối hoặc xây dựng các route API trung gian:
// app/posts/page.tsx
import prisma from '@/lib/prisma'
import Link from 'next/link'
export const revalidate = 3600 // Cache page for 1 hour
export default async function PostsPage() {
const posts = await prisma.post.findMany({
where: { published: true },
include: { author: { select: { name: true, email: true } } },
orderBy: { createdAt: 'desc' },
})
return (
<main className="max-w-4xl mx-auto p-8">
<h1 className="text-3xl font-bold mb-6">Published Articles</h1>
<div className="grid gap-4">
{posts.map((post) => (
<article key={post.id} className="p-5 border rounded-xl shadow-sm">
<h2 className="text-xl font-semibold">{post.title}</h2>
<p className="text-sm text-gray-500">By {post.author.name}</p>
</article>
))}
</div>
</main>
)
}
Để đột biến dữ liệu, khai báo Server Actions với 'use server' và xác thực lại các thẻ cache:
// app/actions/createPost.ts
'use server'
import prisma from '@/lib/prisma'
import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'
export async function createPost(formData: FormData) {
const title = formData.get('title') as string
const content = formData.get('content') as string
const authorId = formData.get('authorId') as string
if (!title || !authorId) {
throw new Error('Title and Author are required')
}
const post = await prisma.post.create({
data: { title, content, published: true, authorId },
})
revalidatePath('/posts')
redirect(`/posts/${post.id}`)
}
Để khởi tạo cơ sở dữ liệu phát triển của bạn, hãy tạo prisma/seed.ts:
// prisma/seed.ts
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
async function main() {
await prisma.post.deleteMany()
await prisma.user.deleteMany()
const user = await prisma.user.create({
data: {
email: 'admin@locionic.com',
name: 'Locionic',
posts: {
create: [
{ title: 'Next.js App Router Architecture', published: true },
{ title: 'Prisma Connection Pooling Guide', published: true },
],
},
},
})
console.log('Database seeded successfully:', user.email)
}
main()
.catch((e) => {
console.error(e)
process.exit(1)
})
.finally(async () => {
await prisma.$disconnect()
})
Thêm cấu hình script khởi tạo vào package.json của bạn:
{
"prisma": {
"seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts"
}
}
Chạy lệnh khởi tạo với npx prisma db seed.
Next.js Fast Refresh xây dựng lại các mô-đun trên mỗi lần thay đổi tệp, tạo một phiên bản PrismaClient mới trên mỗi lần hot reload. Gắn client vào đối tượng toàn cục của Node.js (globalThis.prismaGlobal) đảm bảo chỉ có một phiên bản nhóm kết nối tồn tại trong suốt vòng đời của máy chủ phát triển.
Không, Prisma yêu cầu kết nối socket cơ sở dữ liệu TCP trực tiếp không thể chạy bên trong môi trường trình duyệt. Truy vấn Prisma độc quyền bên trong Server Components, Route Handlers hoặc Server Actions.
Khi triển khai lên các nền tảng serverless như Vercel hoặc AWS Lambda, hãy kết nối thông qua một bộ kết nối nhóm như PgBouncer, Neon Serverless Pooling, Supabase Supavisor hoặc Prisma Accelerate với ?pgbouncer=true&connection_limit=1.
Xác định lệnh prisma.seed trong package.json trỏ đến prisma/seed.ts, sau đó chạy npx prisma db seed để điền dữ liệu demo một cách xác định.
Trong một ứng dụng Next.js App Router sử dụng Prisma, tại sao việc triển khai mẫu singleton toàn cục cho `PrismaClient` lại quan trọng, đặc biệt trong quá trình phát triển cục bộ với Fast Refresh?
Làm sạch log AI CLI, tạo biểu thức cron, giải mã JWT và tính quyền chmod offline.

Hướng dẫn chuẩn production toàn diện cho Next.js App Router: Ranh giới Server Components, caching nâng cao, streaming với Suspense, Server Actions, connection pooling và kiến trúc route cho enterprise.
Đọc tiếp
Hướng dẫn toàn diện về duckdb-wasm trong next.js: phân tích phía máy khách cực nhanh trên 10 triệu hàng với kiến trúc cấp độ sản xuất và các ví dụ mã.
Đọc tiếp
Hướng dẫn toàn diện về các chiến lược database seeding hiện đại sử dụng Prisma và TypeScript, khám phá tính idempotency, tích hợp Faker.js, xử lý dữ liệu quan hệ phức tạp và tối ưu hóa hiệu suất cho các seed script lớn.
Đọc tiếp