•6 min read

Next.js App Router với Prisma: Thiết lập & Kết nối Pooling

Next.js App Router với Prisma: Thiết lập & Kết nối Pooling
Next.js App Router and Prisma Architecture Guide
Audio Briefing
0:00 / 0:00
Part of a Series

Chuỗi kiến trúc Next.js hiện đại

Part 3 of 3

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.


1. Khởi tạo dự án & Prisma Schema

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())
}

Advertisement

2. Ngăn chặn cạn kiệt nhóm kết nối (Global Singleton)

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

3. Tìm nạp dữ liệu trực tiếp trong React Server Components

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>
  )
}

4. Đột biến dữ liệu an toàn kiểu với Server Actions

Để độ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}`)
}

Advertisement

5. Khởi tạo cơ sở dữ liệu tự động

Để 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.


Bạn cũng có thể thích

Share this article:

Stay Updated

Get the latest posts delivered straight to your inbox.

Free Developer Utilities

Free In-Browser Developer Tools

Clean AI CLI logs, build cron expressions, decode JWTs, and calculate chmod permissions offline.

Explore Tools
Advertisement