跳到主要內容

如何在 Next.js 中使用 Prisma ORM

20 分鐘

引言

本指南將向您展示如何在 Next.js 15(一個全棧 React 框架)中使用 Prisma。您將學習如何建立 Prisma Postgres 例項、在 Next.js 中設定 Prisma ORM、處理遷移以及將應用程式部署到 Vercel。

您可以在 GitHub 上找到一個可供部署的示例

先決條件

  • Node.js 18+
  • Vercel 帳戶(如果您想部署應用程式)

1. 設定你的專案

從您想建立專案的目錄中,執行 create-next-app 來建立一個新的 Next.js 應用程式,您將在本指南中使用它。

npx create-next-app@latest nextjs-prisma

系統會提示您回答一些關於專案的問題。選擇所有預設選項。

資訊

作為參考,它們是

  • TypeScript
  • ESLint
  • Tailwind CSS
  • src 目錄
  • App Router
  • Turbopack
  • 無自定義匯入別名

然後,導航到專案目錄

cd nextjs-prisma

2. 安裝和配置 Prisma

2.1. 安裝依賴

要開始使用 Prisma,您需要安裝一些依賴項

npm install prisma tsx --save-dev
npm install @prisma/extension-accelerate @prisma/client

安裝後,在您的專案中初始化 Prisma

npx prisma init --db --output ../app/generated/prisma
資訊

在設定 Prisma Postgres 資料庫時,您需要回答幾個問題。選擇離您位置最近的區域,併為您的資料庫起一個容易記住的名字,例如“我的 __________ 專案”

這將建立

  • 一個包含 schema.prisma 檔案的 prisma 目錄。
  • 一個 Prisma Postgres 資料庫。
  • 專案根目錄下的一個包含 DATABASE_URL.env 檔案。
  • 一個用於生成 Prisma Client 的 output 目錄,路徑為 app/generated/prisma

2.2. 定義你的 Prisma Schema

prisma/schema.prisma 檔案中,新增以下模型

prisma/schema.prisma
generator client {
provider = "prisma-client-js"
output = "../app/generated/prisma"
}

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}

model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
authorId Int
author User @relation(fields: [authorId], references: [id])
}

這建立了兩個模型:UserPost,它們之間存在一對多的關係。

2.3. 配置 Prisma Client 生成器

現在,執行以下命令來建立資料庫表並生成 Prisma Client

npx prisma migrate dev --name init

2.4. 填充資料庫

新增一些種子資料來填充資料庫,包含示例使用者和帖子。

prisma/ 目錄中建立一個名為 seed.ts 的新檔案

prisma/seed.ts
import { PrismaClient, Prisma } from "../app/generated/prisma";

const prisma = new PrismaClient();

const userData: Prisma.UserCreateInput[] = [
{
name: "Alice",
email: "alice@prisma.io",
posts: {
create: [
{
title: "Join the Prisma Discord",
content: "https://pris.ly/discord",
published: true,
},
{
title: "Prisma on YouTube",
content: "https://pris.ly/youtube",
},
],
},
},
{
name: "Bob",
email: "bob@prisma.io",
posts: {
create: [
{
title: "Follow Prisma on Twitter",
content: "https://www.twitter.com/prisma",
published: true,
},
],
},
},
];

export async function main() {
for (const u of userData) {
await prisma.user.create({ data: u });
}
}

main();

現在,透過更新 package.json 來告訴 Prisma 如何執行此指令碼

package.json
{
"name": "nextjs-prisma",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"prisma": {
"seed": "tsx prisma/seed.ts"
},
"dependencies": {
"@prisma/client": "^6.7.0",
"@prisma/extension-accelerate": "^1.3.0",
"next": "15.3.1",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "15.3.1",
"prisma": "^6.7.0",
"tailwindcss": "^4",
"tsx": "^4.19.4",
"typescript": "^5"
}
}
警告

在啟動開發伺服器之前,請注意,如果您使用的是 Next.js v15.2.0 或 v15.2.1,請勿使用 Turbopack,因為存在一個已知問題。透過更新 package.json 來從開發指令碼中移除 Turbopack

package.json
"script":{
"dev": "next dev --turbopack",
"dev": "next dev",
}

在之前或之後的任何版本上都不需要此更改。

最後,執行 prisma db seed 來使用我們在 seed.ts 檔案中定義的初始資料填充您的資料庫。

執行種子指令碼

npx prisma db seed

並開啟 Prisma Studio 檢查你的資料

npx prisma studio

2.5 設定 Prisma Client

現在您已經有了一個包含一些初始資料的資料庫,您可以設定 Prisma Client 並將其連線到您的資料庫。

在專案根目錄下,建立一個新的 lib 目錄並在其中新增一個 prisma.ts 檔案。

mkdir -p lib && touch lib/prisma.ts

現在,將以下程式碼新增到您的 lib/prisma.ts 檔案中

lib/prisma.ts
import { PrismaClient } from '../app/generated/prisma'
import { withAccelerate } from '@prisma/extension-accelerate'

const globalForPrisma = global as unknown as {
prisma: PrismaClient
}

const prisma = globalForPrisma.prisma || new PrismaClient().$extends(withAccelerate())

if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma

export default prisma

此檔案建立了一個 Prisma 客戶端並將其附加到全域性物件上,以便在您的應用程式中只建立一個客戶端例項。這有助於解決在開發模式下將 Prisma ORM 與 Next.js 一起使用時可能出現的熱過載問題。

您將在下一節中使用此客戶端來執行您的第一個查詢。

3. 使用 Prisma ORM 查詢你的資料庫

現在您已經初始化了 Prisma Client,連線到了資料庫,並且有了一些初始資料,您可以開始使用 Prisma ORM 查詢資料了。

在此示例中,您將使應用程式的“主頁”顯示所有使用者。

開啟 app/page.tsx 檔案並將其現有程式碼替換為以下內容

app/page.tsx
export default async function Home() {
return (
<div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center -mt-16">
<h1 className="text-4xl font-bold mb-8 font-[family-name:var(--font-geist-sans)] text-[#333333]">
Superblog
</h1>
<ol className="list-decimal list-inside font-[family-name:var(--font-geist-sans)]">
<li className="mb-2">Alice</li>
<li>Bob</li>
</ol>
</div>
);
}

這為您提供了一個帶有標題和使用者列表的基本頁面。然而,該列表是靜態的,帶有硬編碼值。讓我們更新頁面以從您的資料庫中獲取使用者並使其動態化。

app/page.tsx
import prisma from '@/lib/prisma'

export default async function Home() {
const users = await prisma.user.findMany();
return (
<div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center -mt-16">
<h1 className="text-4xl font-bold mb-8 font-[family-name:var(--font-geist-sans)] text-[#333333]">
Superblog
</h1>
<ol className="list-decimal list-inside font-[family-name:var(--font-geist-sans)]">
{users.map((user) => (
<li key={user.id} className="mb-2">
{user.name}
</li>
))}
</ol>
</div>
);
}

您現在正在匯入客戶端,查詢 User 模型以獲取所有使用者,然後將它們顯示在列表中。

現在您的主頁是動態的,並將顯示來自您資料庫的使用者。

3.1 更新您的資料 (可選)

如果您想看看資料更新時會發生什麼,您可以

  • 透過您選擇的 SQL 瀏覽器更新您的 User
  • 更改您的 seed.ts 檔案以新增更多使用者
  • 更改對 prisma.user.findMany 的呼叫,以重新排序使用者、篩選使用者或類似操作。

只需重新載入頁面,您就會看到更改。

4. 新增一個新的帖子列表頁

您的主頁已正常執行,但您應該新增一個新頁面來顯示所有帖子。

首先在 app 目錄下建立一個新的 posts 目錄,並在其中建立一個新的 page.tsx 檔案。

mkdir -p app/posts && touch app/posts/page.tsx

其次,將以下程式碼新增到 app/posts/page.tsx 檔案中

app/posts/page.tsx
import prisma from "@/lib/prisma";

export default async function Posts() {
return (
<div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center -mt-16">
<h1 className="text-4xl font-bold mb-8 font-[family-name:var(--font-geist-sans)] text-[#333333]">
Posts
</h1>
<ul className="font-[family-name:var(--font-geist-sans)] max-w-2xl space-y-4">
<li>My first post</li>
</ul>
</div>
);
}

現在 localhost:3000/posts 將載入,但內容再次是硬編碼的。讓我們更新它使其動態化,類似於主頁

app/posts/page.tsx
import prisma from "@/lib/prisma";

export default async function Posts() {
const posts = await prisma.post.findMany({
include: {
author: true,
},
});

return (
<div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center -mt-16">
<h1 className="text-4xl font-bold mb-8 font-[family-name:var(--font-geist-sans)] text-[#333333]">
Posts
</h1>
<ul className="font-[family-name:var(--font-geist-sans)] max-w-2xl space-y-4">
{posts.map((post) => (
<li key={post.id}>
<span className="font-semibold">{post.title}</span>
<span className="text-sm text-gray-600 ml-2">
by {post.author.name}
</span>
</li>
))}
</ul>
</div>
);
}

這與主頁類似,但它顯示的是帖子而不是使用者。您還可以看到,您在 Prisma Client 查詢中使用了 include 來獲取每個帖子的作者,這樣您就可以顯示作者的姓名。

這種“列表檢視”是 Web 應用程式中最常見的模式之一。您將向您的應用程式新增另外兩個您也通常需要的頁面:“詳細檢視”和“建立檢視”。

5. 新增一個新的帖子詳情頁

為了完善帖子列表頁,您將新增一個帖子詳情頁。

posts 目錄中,建立一個新的 [id] 目錄並在其中建立一個新的 page.tsx 檔案。

mkdir -p app/posts/[id] && touch app/posts/[id]/page.tsx

此頁面將顯示單個帖子的標題、內容和作者。就像您的其他頁面一樣,將以下程式碼新增到 app/posts/new/page.tsx 檔案中

app/posts/[id]/page.tsx
import prisma from "@/lib/prisma";

export default async function Post({ params }: { params: Promise<{ id: string }> }) {
return (
<div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center -mt-16">
<article className="max-w-2xl space-y-4 font-[family-name:var(--font-geist-sans)]">
<h1 className="text-4xl font-bold mb-8 text-[#333333]">My first post</h1>
<p className="text-gray-600 text-center">by Anonymous</p>
<div className="prose prose-gray mt-8">
No content available.
</div>
</article>
</div>
);
}

和以前一樣,這個頁面是靜態的,內容是硬編碼的。讓我們根據傳遞給頁面的 params 來更新它,使其動態化

app/posts/[id]/page.tsx
import prisma from "@/lib/prisma";
import { notFound } from "next/navigation";

export default async function Post({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const post = await prisma.post.findUnique({
where: { id: parseInt(id) },
include: {
author: true,
},
});

if (!post) {
notFound();
}

return (
<div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center -mt-16">
<article className="max-w-2xl space-y-4 font-[family-name:var(--font-geist-sans)]">
<h1 className="text-4xl font-bold mb-8 text-[#333333]">{post.title}</h1>
<p className="text-gray-600 text-center">by {post.author.name}</p>
<div className="prose prose-gray mt-8">
{post.content || "No content available."}
</div>
</article>
</div>
);
}

這裡有很多變化,我們來分解一下

  • 您正在使用 Prisma Client 根據其 id 獲取帖子,該 id 來自 params 物件。
  • 如果帖子不存在(可能已被刪除,或者您輸入了錯誤的 ID),您將呼叫 notFound() 以顯示 404 頁面。
  • 然後顯示帖子的標題、內容和作者。如果帖子沒有內容,則顯示佔位符訊息。

雖然頁面不太美觀,但這是一個好的開始。嘗試導航到 localhost:3000/posts/1localhost:3000/posts/2。您也可以透過導航到 localhost:3000/posts/999 來測試 404 頁面。

6. 新增一個新的帖子建立頁面

為了完善您的應用程式,您將為帖子新增一個“建立”頁面。這將允許您撰寫自己的帖子並將其儲存到資料庫中。

與其他頁面一樣,您將從一個靜態頁面開始,然後將其更新為動態頁面。

mkdir -p app/posts/new && touch app/posts/new/page.tsx

現在,將以下程式碼新增到 app/posts/new/page.tsx 檔案中

app/posts/new/page.tsx
import Form from "next/form";

export default function NewPost() {
async function createPost(formData: FormData) {
"use server";

const title = formData.get("title") as string;
const content = formData.get("content") as string;
}

return (
<div className="max-w-2xl mx-auto p-4">
<h1 className="text-2xl font-bold mb-6">Create New Post</h1>
<Form action={createPost} className="space-y-6">
<div>
<label htmlFor="title" className="block text-lg mb-2">
Title
</label>
<input
type="text"
id="title"
name="title"
placeholder="Enter your post title"
className="w-full px-4 py-2 border rounded-lg"
/>
</div>
<div>
<label htmlFor="content" className="block text-lg mb-2">
Content
</label>
<textarea
id="content"
name="content"
placeholder="Write your post content here..."
rows={6}
className="w-full px-4 py-2 border rounded-lg"
/>
</div>
<button
type="submit"
className="w-full bg-blue-500 text-white py-3 rounded-lg hover:bg-blue-600"
>
Create Post
</button>
</Form>
</div>
);
}

這個表單看起來不錯,但它還沒有任何功能。讓我們更新 createPost 函式以將帖子儲存到資料庫中

app/posts/new/page.tsx
import Form from "next/form";
import prisma from "@/lib/prisma";
import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";

export default function NewPost() {
async function createPost(formData: FormData) {
"use server";

const title = formData.get("title") as string;
const content = formData.get("content") as string;

await prisma.post.create({
data: {
title,
content,
authorId: 1,
},
});

revalidatePath("/posts");
redirect("/posts");
}

return (
<div className="max-w-2xl mx-auto p-4">
<h1 className="text-2xl font-bold mb-6">Create New Post</h1>
<Form action={createPost} className="space-y-6">
<div>
<label htmlFor="title" className="block text-lg mb-2">
Title
</label>
<input
type="text"
id="title"
name="title"
placeholder="Enter your post title"
className="w-full px-4 py-2 border rounded-lg"
/>
</div>
<div>
<label htmlFor="content" className="block text-lg mb-2">
Content
</label>
<textarea
id="content"
name="content"
placeholder="Write your post content here..."
rows={6}
className="w-full px-4 py-2 border rounded-lg"
/>
</div>
<button
type="submit"
className="w-full bg-blue-500 text-white py-3 rounded-lg hover:bg-blue-600"
>
Create Post
</button>
</Form>
</div>
);
}

這個頁面現在有一個功能性的表單!當您提交表單時,它將在資料庫中建立一個新帖子,並重定向您到帖子列表頁面。

您還添加了一個 revalidatePath 呼叫,以重新驗證帖子列表頁面,使其能與新帖子一起更新。這樣每個人都可以立即閱讀新帖子。

嘗試導航到 localhost:3000/posts/new 並提交表單。

7. 將您的應用程式部署到 Vercel (可選)

將您的應用程式部署到 Vercel 的最快方法是使用 Vercel CLI

首先,安裝 Vercel CLI

npm install -g vercel

然後,執行 vercel login 登入您的 Vercel 帳戶。

vercel login

在部署之前,您還需要告訴 Vercel 確保生成 Prisma Client。您可以透過在 package.json 檔案中新增 postinstall 指令碼來完成此操作。

package.json
{
"name": "nextjs-prisma",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"postinstall": "prisma generate --no-engine",
"start": "next start",
"lint": "next lint"
},
"prisma": {
"seed": "tsx prisma/seed.ts"
},
"dependencies": {
"@prisma/client": "^6.2.1",
"@prisma/extension-accelerate": "^1.2.1",
"next": "15.1.4",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "15.1.4",
"postcss": "^8",
"prisma": "^6.2.1",
"tailwindcss": "^3.4.1",
"tsx": "^4.19.2",
"typescript": "^5"
}
}
注意

如果您未使用 Prisma Postgres,則需要從命令中刪除 --no-engine 標誌。

此更改後,您可以透過執行 vercel 將應用程式部署到 Vercel。

vercel

部署完成後,您可以透過 Vercel 提供的 URL 訪問您的應用程式。恭喜,您已成功部署了一個帶有 Prisma ORM 的 Next.js 應用程式!

8. 下一步

現在您已經有了一個使用 Prisma ORM 的 Next.js 應用程式,以下是一些可以擴充套件和改進您應用程式的方法

  • 新增身份驗證以保護您的路由
  • 新增編輯和刪除帖子的功能
  • 為帖子新增評論
  • 使用 Prisma Studio 進行視覺化資料庫管理

瞭解更多資訊


與 Prisma 保持聯絡

透過與以下方面建立聯絡,繼續您的 Prisma 之旅 我們的活躍社群。保持資訊更新,參與其中,並與其他開發者協作

我們真心重視您的參與,並期待您成為我們社群的一份子!

© . This site is unofficial and not affiliated with Prisma Data, Inc.