如何使用Next.js?+?Prisma?+?MySQL開發(fā)全棧項(xiàng)目
本文介紹如何用 Next.js、Prisma、MySQL 搭建全棧應(yīng)用:包含 MySQL 與 MySQL Workbench 的安裝與基本使用、創(chuàng)建項(xiàng)目、目錄含義、數(shù)據(jù)庫建模、Migrations(遷移) 的工作方式與命令,并附代碼示例。
一、技術(shù)棧分工
| 技術(shù) | 作用 |
|---|---|
| Next.js | React 框架:路由、SSR、服務(wù)端組件、Route Handlers(API) |
| Prisma | ORM:類型安全查詢、Schema 定義、遷移版本管理 |
| MySQL | 關(guān)系型數(shù)據(jù)庫:表、外鍵、事務(wù)、索引 |
二、如何創(chuàng)建 Next.js 項(xiàng)目
2.1 前置條件
- 已安裝 Node.js(建議 LTS 版本)
- 包管理器任選:
npm、pnpm或yarn
2.2 使用官方腳手架
在項(xiàng)目父目錄執(zhí)行:
npx create-next-app@latest my-app
交互式選項(xiàng)說明(根據(jù)版本可能略有差異):
| 選項(xiàng) | 建議 | 說明 |
|---|---|---|
| TypeScript | Yes | 與 Prisma、大型項(xiàng)目更匹配 |
| ESLint | Yes | 統(tǒng)一代碼風(fēng)格 |
| Tailwind CSS | 按需 | UI 快速開發(fā)可選用 |
src/ directory | 按需 | 若選 Yes,應(yīng)用代碼在 src/app 下 |
| App Router | Yes | 當(dāng)前推薦,與本文目錄說明一致 |
| Turbopack | 按需 | 開發(fā)時(shí)可選更快的打包器 |
進(jìn)入項(xiàng)目并安裝依賴:
cd my-app npm install
2.3 本地運(yùn)行
npm run dev
瀏覽器訪問 http://localhost:3000。默認(rèn)端口為 3000。
2.4 常用腳本(package.json)
| 命令 | 作用 |
|---|---|
npm run dev | 開發(fā)模式,熱更新 |
npm run build | 生產(chǎn)構(gòu)建 |
npm run start | 啟動(dòng)生產(chǎn)構(gòu)建后的服務(wù)(需先 build) |
npm run lint | 運(yùn)行 ESLint |
三、典型目錄與文件說明(App Router)
以下以 未使用 src/ 的默認(rèn)結(jié)構(gòu)為例;若創(chuàng)建時(shí)選了 src/,則把 app、components 等放在 src/ 下即可。
my-app/ ├── app/ # App Router:頁面、布局、路由 │ ├── layout.tsx # 根布局(全局 HTML 殼、字體、Provider) │ ├── page.tsx # 路由 "/" 的首頁 │ ├── globals.css # 全局樣式 │ ├── loading.tsx # 可選:該路由段的加載 UI(Suspense) │ ├── error.tsx # 可選:該路由段的錯(cuò)誤邊界 │ └── api/ # Route Handlers(HTTP API) │ └── users/ │ └── route.ts # 對(duì)應(yīng)路徑 /api/users ├── public/ # 靜態(tài)資源:favicon、圖片等,URL 根路徑直接訪問 ├── prisma/ │ ├── schema.prisma # 數(shù)據(jù)模型與數(shù)據(jù)庫連接配置 │ └── migrations/ # 遷移歷史(由 prisma migrate 生成,勿手改 SQL 邏輯) ├── lib/ # 常用:工具函數(shù)、Prisma 單例等(自建) │ └── prisma.ts ├── components/ # 可復(fù)用 UI 組件(自建) ├── .env # 本地環(huán)境變量(勿提交密鑰) ├── .env.example # 環(huán)境變量示例(可提交) ├── next.config.ts / .js # Next 構(gòu)建與運(yùn)行時(shí)配置 ├── tsconfig.json # TypeScript 配置 └── package.json
3.1app/目錄
page.tsx:該路由的頁面 UI;文件夾名即 URL 路徑段。layout.tsx:嵌套布局,子路由共享外殼(導(dǎo)航欄、側(cè)邊欄等)。route.ts:只處理 HTTP 方法(GET、POST 等),不渲染頁面,用于 REST API。- 默認(rèn)導(dǎo)出為 Server Component,可直接
async并訪問數(shù)據(jù)庫;需要瀏覽器事件、hooks 時(shí)在文件頂部加"use client"。
3.2public/
放不需要經(jīng)過打包處理的靜態(tài)文件,例如 public/logo.png 對(duì)應(yīng)訪問路徑 /logo.png。
3.3prisma/
schema.prisma:定義數(shù)據(jù)源、生成器、Model(表結(jié)構(gòu))。migrations/:每次執(zhí)行prisma migrate dev(或生產(chǎn)用deploy)產(chǎn)生的遷移記錄,見下文「Migrations 詳解」。
3.4 根目錄配置文件
next.config.*:圖片域名、重定向、實(shí)驗(yàn)特性等。tsconfig.json:路徑別名(如@/*)常在這里配置,與import有關(guān)。
四、MySQL 安裝與 MySQL Workbench
4.1 官方下載地址
以下均為 Oracle MySQL 官方站點(diǎn),請(qǐng)從 Downloads 中選擇操作系統(tǒng)與安裝包類型。
| 軟件 | 說明 | 下載頁 |
|---|---|---|
| MySQL Community Server | 數(shù)據(jù)庫服務(wù)本體(必裝,供本地/服務(wù)器運(yùn)行 MySQL) | dev.mysql.com/downloads/m… |
| MySQL Installer(僅 Windows) | 一站式安裝器,可勾選 Server、Workbench、Shell 等 | dev.mysql.com/downloads/i… |
| MySQL Workbench | 圖形化管理與 SQL 開發(fā)工具(可選但強(qiáng)烈推薦) | dev.mysql.com/downloads/w… |
說明:
- 若使用 MySQL Installer for Windows,通常一次勾選 MySQL Server + MySQL Workbench 即可,無需單獨(dú)下載 Workbench。
- macOS 可使用官網(wǎng) DMG 或包管理器(如 Homebrew:
brew install mysql);Linux 常用發(fā)行版?zhèn)}庫或官網(wǎng) APT/YUM 倉庫,Workbench 可單獨(dú)安裝。
4.2 在 Windows 上安裝 MySQL Server(常見流程)
以下以 MySQL Installer 為例(適合本機(jī)開發(fā)):
- 打開 MySQL Installer 下載頁,選擇 Windows (x86, 64-bit), MSI Installer(體積較大的完整安裝包或在線安裝包均可)。
- 運(yùn)行安裝程序,選擇安裝類型:
- Developer Default:會(huì)安裝 Server、Workbench、Shell 等,適合開發(fā)。
- 若只需數(shù)據(jù)庫服務(wù),可選 Server only。
- 按向?qū)?zhí)行 Execute 安裝所選組件。
- 在 Type and Networking 中保持默認(rèn)端口 3306(除非端口沖突,需與 Prisma 的
DATABASE_URL一致)。 - 在 Authentication 中保持默認(rèn) Strong Password(推薦)。
- 設(shè)置 root 用戶密碼并牢記;后續(xù) Workbench、Prisma 連接都會(huì)用到。
- 將 MySQL 配置為 Windows Service,并勾選 Start the MySQL Server at System Startup(開機(jī)自啟,便于開發(fā))。
- 完成安裝。
4.3 安裝后如何確認(rèn) MySQL 已運(yùn)行
- 服務(wù):按
Win + R,輸入services.msc,查找 MySQL 或 MySQL80 等服務(wù)名,狀態(tài)應(yīng)為 正在運(yùn)行。 - 命令行(若安裝時(shí)勾選了命令行客戶端且已加入 PATH):
mysql --version
- 若無法直接執(zhí)行
mysql,可在「開始菜單」打開 MySQL Command Line Client,用 root 密碼登錄測(cè)試。
4.4 安裝 MySQL Workbench
- 若已通過 MySQL Installer 勾選 Workbench,可跳過單獨(dú)安裝。
- 否則打開 Workbench 下載頁,選擇 Windows / macOS / Linux 對(duì)應(yīng)安裝包,按向?qū)О惭b即可。
4.5 使用 MySQL Workbench 連接數(shù)據(jù)庫
- 打開 MySQL Workbench。
- 主界面 MySQL Connections 區(qū)域點(diǎn)擊 「+」 或 MySQL Connections 旁的加號(hào),新建連接。
- 填寫連接參數(shù)(本地開發(fā)常見值):
| 字段 | 典型值 | 說明 |
|---|---|---|
| Connection Name | 任意名稱,如 Local Dev | 僅顯示用 |
| Hostname | 127.0.0.1 或 localhost | 本機(jī)數(shù)據(jù)庫 |
| Port | 3306 | 與安裝時(shí)一致 |
| Username | root | 或你創(chuàng)建的其他用戶 |
| Password | — | 點(diǎn)擊 Store in Keychain / Vault… 保存密碼,避免每次輸入 |
- 點(diǎn)擊 Test Connection,提示成功即可 OK 保存。
- 雙擊該連接進(jìn)入主工作區(qū):左側(cè)為 Navigator(庫表列表),中間為 SQL 編輯與結(jié)果區(qū)。

4.6 MySQL Workbench 基本操作
| 操作 | 步驟 |
|---|---|
| 創(chuàng)建數(shù)據(jù)庫(Schema) | 左側(cè) Schemas 面板空白處右鍵 → Create Schema… → 輸入庫名(如 mydb)→ Apply;或在 Query 窗口執(zhí)行:CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; |
| 執(zhí)行 SQL | 頂部 File → New Query Tab 或工具欄新建查詢標(biāo)簽 → 輸入 SQL → 選中語句 → 點(diǎn)擊 閃電圖標(biāo) 執(zhí)行(或 Ctrl + Enter)。 |
| 查看表數(shù)據(jù) | Schemas 中展開數(shù)據(jù)庫 → Tables → 表名右鍵 → Select Rows - Limit 1000。 |
| 查看表結(jié)構(gòu) | 表名右鍵 → Table Inspector 或 Alter Table。 |
| 新建用戶/授權(quán)(進(jìn)階) | 菜單 Server → Users and Privileges(需相應(yīng)權(quán)限);開發(fā)階段常用 root,生產(chǎn)環(huán)境應(yīng)使用最小權(quán)限賬號(hào)。 |
與Prisma配合時(shí):先在 Workbench 里創(chuàng)建空庫(如mydb),再在項(xiàng)目.env中把DATABASE_URL指向該庫;表結(jié)構(gòu)通常交給prisma migrate管理,無需在 Workbench 里手工建每張表(除非你不用遷移、純手寫 SQL)。
五、接入 Prisma 與 MySQL
5.1 安裝
npm install prisma @prisma/client npx prisma init
prisma init 會(huì)創(chuàng)建 prisma/schema.prisma,并在項(xiàng)目根目錄提示創(chuàng)建 .env。
5.2 配置連接串(.env)
DATABASE_URL="mysql://用戶名:密碼@主機(jī):3306/數(shù)據(jù)庫名"
示例(本地 MySQL):
DATABASE_URL="mysql://root:secret@localhost:3306/mydb"
注意:將 .env 加入 .gitignore,倉庫中只保留 .env.example(不含真實(shí)密碼)。
5.3schema.prisma示例
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
createdAt DateTime @default(now())
}
model Post {
id Int @id @default(autoincrement())
title String
published Boolean @default(false)
authorId Int
author User @relation(fields: [authorId], references: [id])
createdAt DateTime @default(now())
}
5.4 生成 Client
修改 schema 后執(zhí)行:
npx prisma generate
會(huì)依據(jù)當(dāng)前 schema.prisma 生成 node_modules/.prisma/client 等,供 TypeScript 使用。
六、Migrations(數(shù)據(jù)庫遷移)詳解
Migration 指:把「數(shù)據(jù)庫結(jié)構(gòu)應(yīng)該怎樣」從 Prisma Schema 出發(fā),變成 可重復(fù)執(zhí)行、可版本控制 的 SQL 變更步驟。團(tuán)隊(duì)協(xié)作時(shí),所有人用同一套遷移文件,就能把本地/測(cè)試/生產(chǎn)數(shù)據(jù)庫結(jié)構(gòu)對(duì)齊。
6.1 為什么需要遷移
- 可回顧:每次結(jié)構(gòu)變更都有對(duì)應(yīng) SQL 文件與命名(如
20240323120000_init)。 - 可復(fù)現(xiàn):新同事或新環(huán)境執(zhí)行同一套遷移即可得到一致表結(jié)構(gòu)。
- 與「只改庫不改文件」對(duì)比:手動(dòng)在 Navicat 里改表,無法自動(dòng)同步到其他人的機(jī)器,也容易與
schema.prisma不一致。
6.2 開發(fā)環(huán)境常用命令:prisma migrate dev
首次或每次修改 schema.prisma 后:
npx prisma migrate dev --name 描述本次變更的英文短語
例如:
npx prisma migrate dev --name init npx prisma migrate dev --name add_post_published_index
該命令會(huì):
- 對(duì)比當(dāng)前 schema 與數(shù)據(jù)庫狀態(tài),生成新的 SQL 遷移文件到
prisma/migrations/<時(shí)間戳>_<name>/migration.sql。 - 應(yīng)用這些遷移到當(dāng)前
DATABASE_URL指向的數(shù)據(jù)庫。 - 觸發(fā)
prisma generate,更新 Prisma Client。
適合:本地開發(fā)、共享開發(fā)數(shù)據(jù)庫(團(tuán)隊(duì)約定好同一條 DATABASE_URL 時(shí)要注意沖突)。
6.3 生產(chǎn) / CI:prisma migrate deploy
在生產(chǎn)構(gòu)建流程或服務(wù)器上,不要用 migrate dev(它會(huì)交互式、且偏向開發(fā)工作流)。應(yīng)使用:
npx prisma migrate deploy
作用:只執(zhí)行 prisma/migrations 里尚未應(yīng)用的遷移,不根據(jù)當(dāng)前數(shù)據(jù)庫反向改 schema,適合流水線與只讀權(quán)限受限的環(huán)境。
典型順序:
- 構(gòu)建應(yīng)用:
npm run build - 部署前或啟動(dòng)前:
npx prisma migrate deploy - 啟動(dòng):
npm run start
6.4db push與migrate的區(qū)別
| 命令 | 適用場(chǎng)景 |
|---|---|
prisma migrate dev | 有遷移歷史、團(tuán)隊(duì)需要版本化 SQL;推薦正式項(xiàng)目 |
prisma db push | 快速把 schema 推到數(shù)據(jù)庫,不生成遷移文件;原型、個(gè)人玩具項(xiàng)目或明確不保留遷移歷史時(shí)可用 |
生產(chǎn)環(huán)境應(yīng)依賴 migrate deploy + 已提交的 migrations 文件夾,而不是 db push。
6.5prisma/migrations目錄里有什么
prisma/migrations/
├── migration_lock.toml # 鎖定數(shù)據(jù)庫提供方(如 mysql)
└── 20240323120000_init/
└── migration.sql # 本次遷移的 SQL(由 Prisma 生成)
migration.sql:可閱讀、可審計(jì);一般不要手改(除非你很清楚后果)。- 新增遷移 = 新文件夾 + 新 SQL,按時(shí)間順序應(yīng)用。
6.6 常見注意點(diǎn)
- 修改已部署過的遷移文件可能導(dǎo)致校驗(yàn)失?。灰焉暇€的變更應(yīng)通過新遷移追加修改。
- 備份:生產(chǎn)執(zhí)行
migrate deploy前應(yīng)有數(shù)據(jù)庫備份策略。 - Serverless(如部分 Vercel 函數(shù))需注意 MySQL 連接數(shù);必要時(shí)使用連接池或官方推薦的托管方案。
七、Prisma Client 單例(避免開發(fā)環(huán)境連接耗盡)
lib/prisma.ts:
import { PrismaClient } from "@prisma/client";
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };
export const prisma =
globalForPrisma.prisma ??
new PrismaClient({
log: process.env.NODE_ENV === "development" ? ["query", "error", "warn"] : ["error"],
});
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
八、在 Server Component 中查詢
app/users/page.tsx:
import { prisma } from "@/lib/prisma";
export default async function UsersPage() {
const users = await prisma.user.findMany({
orderBy: { createdAt: "desc" },
include: { posts: true },
});
return (
<main>
<h1>用戶列表</h1>
<ul>
{users.map((u) => (
<li key={u.id}>
{u.name ?? u.email} — 文章數(shù):{u.posts.length}
</li>
))}
</ul>
</main>
);
}
九、Route Handler 示例
app/api/users/route.ts:
import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
export async function GET() {
const users = await prisma.user.findMany();
return NextResponse.json(users);
}
export async function POST(request: Request) {
const body = await request.json();
const { email, name } = body as { email: string; name?: string };
const user = await prisma.user.create({
data: { email, name },
});
return NextResponse.json(user, { status: 201 });
}
十、數(shù)據(jù)的修改與刪除接口(Route Handlers)
單條資源的 更新、刪除 通常不放在集合路徑 /api/users 上,而是使用 動(dòng)態(tài)路由 /api/users/[id]:路徑里的 id 對(duì)應(yīng)數(shù)據(jù)庫主鍵(或業(yè)務(wù)唯一標(biāo)識(shí)),與 REST 習(xí)慣一致(對(duì)「某一個(gè)用戶」做 PATCH/DELETE)。
10.1 路由文件位置
在 App Router 中新建:
app/api/users/[id]/route.ts
同一文件內(nèi)可導(dǎo)出 PATCH(部分更新)、PUT(全量更新,可選)、DELETE(刪除)。Next.js 按 HTTP 方法分發(fā)到對(duì)應(yīng)導(dǎo)出函數(shù)。
10.2PATCH:部分更新(常用)
客戶端只傳需要改的字段(例如只改 name),服務(wù)端用 prisma.user.update 合并進(jìn)數(shù)據(jù)庫。
要點(diǎn):
where:用id定位一行;若不存在,Prisma 拋出P2025(Record not found),應(yīng)轉(zhuǎn)為 404。data:只寫入請(qǐng)求體里出現(xiàn)的字段,避免把未傳字段覆蓋成null(需自行從body解構(gòu)并組裝data)。
app/api/users/[id]/route.ts 示例(Next.js 15+ 中 params 為 Promise,需 await):
import { NextResponse } from "next/server";
import { Prisma } from "@prisma/client";
import { prisma } from "@/lib/prisma";
type Ctx = { params: Promise<{ id: string }> };
export async function PATCH(request: Request, ctx: Ctx) {
const { id } = await ctx.params;
const userId = Number(id);
if (Number.isNaN(userId)) {
return NextResponse.json({ error: "Invalid id" }, { status: 400 });
}
const body = await request.json();
const { name, email } = body as { name?: string; email?: string };
const data: { name?: string; email?: string } = {};
if (name !== undefined) data.name = name;
if (email !== undefined) data.email = email;
if (Object.keys(data).length === 0) {
return NextResponse.json({ error: "No fields to update" }, { status: 400 });
}
try {
const user = await prisma.user.update({
where: { id: userId },
data,
});
return NextResponse.json(user);
} catch (e) {
if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === "P2025") {
return NextResponse.json({ error: "User not found" }, { status: 404 });
}
throw e;
}
}
調(diào)用示例(前端或其他客戶端):
PATCH /api/users/1 HTTP/1.1
Content-Type: application/json
{"name": "新名字"}
10.3PUT:全量更新(可選)
語義上 PUT 常表示「用請(qǐng)求體替換整個(gè)資源」。若 User 只有 email、name 等少量字段,可以在服務(wù)端規(guī)定:必須帶上全部可寫字段,否則 400;再執(zhí)行 update。
若業(yè)務(wù)上更關(guān)心「只改部分字段」,優(yōu)先用 PATCH,避免客戶端漏傳字段導(dǎo)致誤清空。
10.4DELETE:刪除一條記錄
使用 prisma.user.delete,成功時(shí)返回 204 No Content(無響應(yīng)體)較常見;也可返回 200 并帶上已刪除對(duì)象的 JSON,按團(tuán)隊(duì)約定即可。
import { NextResponse } from "next/server";
import { Prisma } from "@prisma/client";
import { prisma } from "@/lib/prisma";
type Ctx = { params: Promise<{ id: string }> };
export async function DELETE(_request: Request, ctx: Ctx) {
const { id } = await ctx.params;
const userId = Number(id);
if (Number.isNaN(userId)) {
return NextResponse.json({ error: "Invalid id" }, { status: 400 });
}
try {
await prisma.user.delete({
where: { id: userId },
});
return new NextResponse(null, { status: 204 });
} catch (e) {
if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === "P2025") {
return NextResponse.json({ error: "User not found" }, { status: 404 });
}
throw e;
}
}
說明:若表上有 外鍵約束(例如 Post 依賴 User),刪除用戶可能觸發(fā) Prisma/MySQL 外鍵錯(cuò)誤(如 P2003),需在業(yè)務(wù)層先刪子表數(shù)據(jù)、或改用數(shù)據(jù)庫 ON DELETE CASCADE、或禁止刪除,并對(duì)該錯(cuò)誤返回 409 等狀態(tài)碼。
10.5 按條件批量更新 / 刪除(updateMany/deleteMany)
不通過 id 單條操作,而是按條件批量處理時(shí)使用:
// 將所有未發(fā)布文章標(biāo)為已發(fā)布(示例)
await prisma.post.updateMany({
where: { published: false },
data: { published: true },
});
// 刪除某郵箱前綴的測(cè)試用戶(慎用,生產(chǎn)需強(qiáng)校驗(yàn))
await prisma.user.deleteMany({
where: { email: { startsWith: "test_" } },
});
要點(diǎn):updateMany / deleteMany 不會(huì)在 0 行匹配時(shí)拋錯(cuò);若接口需要「至少刪一行」再返回 404,需在執(zhí)行后根據(jù) count 自行判斷。
10.6 Next.js 14 與params類型說明
若項(xiàng)目仍為 Next.js 14,部分版本中 params 為同步對(duì)象,簽名可寫為 { params: { id: string } } 且不使用 await params。升級(jí)到 Next.js 15 后請(qǐng)改為 params: Promise<...> 并 await params,與官方 Route Handlers 類型一致。
10.7 前端請(qǐng)求代碼(修改與刪除)
以下使用瀏覽器原生 fetch,適用于 客戶端組件(文件頂部需加 "use client")。請(qǐng)求路徑與上文 Route Handler 一致:/api/users/[id]。
要點(diǎn):
PATCH:設(shè)置Content-Type: application/json,body為JSON.stringify后的對(duì)象。DELETE:若服務(wù)端返回 204 No Content,沒有響應(yīng)體,不要調(diào)用response.json(),根據(jù)response.ok或response.status === 204判斷成功。- 失敗時(shí)(4xx/5xx)若接口返回 JSON 錯(cuò)誤信息,可
await response.json()讀?。ㄗ⒁庀扰袛?Content-Type或try/catch)。
封裝函數(shù)(可放在lib/user-api.ts或組件同文件內(nèi))
/** 部分更新用戶(對(duì)應(yīng) PATCH /api/users/:id) */
export async function updateUser(
id: number,
data: { name?: string; email?: string }
): Promise<{ id: number; name: string | null; email: string }> {
const res = await fetch(`/api/users/${id}`, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
const payload = await res.json().catch(() => ({}));
if (!res.ok) {
const message =
typeof payload === "object" && payload !== null && "error" in payload
? String((payload as { error: unknown }).error)
: res.statusText;
throw new Error(message || `HTTP ${res.status}`);
}
return payload as { id: number; name: string | null; email: string };
}
/** 刪除用戶(對(duì)應(yīng) DELETE /api/users/:id,成功為 204 無 body) */
export async function deleteUser(id: number): Promise<void> {
const res = await fetch(`/api/users/${id}`, {
method: "DELETE",
});
if (res.ok) {
return;
}
let message = res.statusText;
try {
const payload = (await res.json()) as { error?: string };
if (payload.error) message = payload.error;
} catch {
/* 無 JSON 時(shí)忽略 */
}
throw new Error(message || `HTTP ${res.status}`);
}
在客戶端組件中調(diào)用(示例)
"use client";
import { useState } from "react";
import { deleteUser, updateUser } from "@/lib/user-api";
export function UserActions({ userId }: { userId: number }) {
const [loading, setLoading] = useState<"patch" | "delete" | null>(null);
const [error, setError] = useState<string | null>(null);
async function handleRename() {
setError(null);
setLoading("patch");
try {
const user = await updateUser(userId, { name: "新名字" });
console.log("更新成功", user);
// 例如:router.refresh() 或更新本地列表狀態(tài)
} catch (e) {
setError(e instanceof Error ? e.message : "更新失敗");
} finally {
setLoading(null);
}
}
async function handleDelete() {
if (!confirm("確定刪除該用戶?")) return;
setError(null);
setLoading("delete");
try {
await deleteUser(userId);
console.log("刪除成功");
// 例如:跳轉(zhuǎn)列表頁或從狀態(tài)中移除
} catch (e) {
setError(e instanceof Error ? e.message : "刪除失敗");
} finally {
setLoading(null);
}
}
return (
<div>
{error && <p role="alert">{error}</p>}
<button type="button" onClick={handleRename} disabled={loading !== null}>
{loading === "patch" ? "更新中…" : "修改名字"}
</button>
<button type="button" onClick={handleDelete} disabled={loading !== null}>
{loading === "delete" ? "刪除中…" : "刪除"}
</button>
</div>
);
}
說明:若列表數(shù)據(jù)來自 Server Component,刪除/更新成功后可在子組件中調(diào)用 import { useRouter } from "next/navigation" 的 router.refresh(),讓服務(wù)端重新渲染最新數(shù)據(jù);或使用全局狀態(tài) / React Query 等自行同步列表。
十一、事務(wù)示例
import { prisma } from "@/lib/prisma";
export async function createUserWithPost(email: string, postTitle: string) {
return prisma.$transaction(async (tx) => {
const user = await tx.user.create({
data: { email },
});
await tx.post.create({
data: {
title: postTitle,
authorId: user.id,
},
});
return user;
});
}
十二、小結(jié)
- 從官網(wǎng)安裝 MySQL Server 與 MySQL Workbench,用 Workbench 測(cè)試連接、建庫、執(zhí)行 SQL 與查看表數(shù)據(jù);下載頁:MySQL Community Server、MySQL Installer(Windows)、MySQL Workbench。
- 用
create-next-app初始化項(xiàng)目,App Router 下頁面在app/,API 在app/api/.../route.ts。 - Prisma 用
schema.prisma定義模型;migrate dev在開發(fā)中生成并應(yīng)用遷移;migrate deploy在生產(chǎn)/CI 應(yīng)用已提交的遷移。 - MySQL 通過
DATABASE_URL連接;全棧鏈路為:Next(服務(wù)端)→ Prisma Client → MySQL。 - 單條資源的修改與刪除使用
app/api/.../[id]/route.ts,導(dǎo)出PATCH/DELETE(可選PUT);不存在時(shí)處理 PrismaP2025返回 404;批量操作用updateMany/deleteMany。前端在客戶端組件中用fetch發(fā)PATCH(JSON body)與DELETE,204 無響應(yīng)體時(shí)不要response.json()。 - prisma 官網(wǎng):www.prisma.io/docs
- next 官網(wǎng) nextjs.frontendx.cn/
到此這篇關(guān)于如何使用Next.js+Prisma+MySQL開發(fā)全棧項(xiàng)目的文章就介紹到這了,更多相關(guān)Next.js + Prisma + MySQL全棧項(xiàng)目?jī)?nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
React router動(dòng)態(tài)加載組件之適配器模式的應(yīng)用詳解
這篇文章主要介紹了React router動(dòng)態(tài)加載組件之適配器模式的應(yīng)用 ,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-09-09
詳解React?Native項(xiàng)目中啟用Hermes引擎
這篇文章主要為大家介紹了React?Native項(xiàng)目中啟用Hermes引擎實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
使用 React 和 Threejs 創(chuàng)建一個(gè)VR全景項(xiàng)目的過程詳解
這篇文章主要介紹了使用 React 和 Threejs 創(chuàng)建一個(gè)VR全景項(xiàng)目的過程詳解,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04
學(xué)習(xí)ahooks useRequest并實(shí)現(xiàn)手寫
這篇文章主要為大家介紹了學(xué)習(xí)ahooks useRequest并實(shí)現(xiàn)手寫示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
React中常見的TypeScript定義實(shí)戰(zhàn)教程
這篇文章主要介紹了React中常見的TypeScript定義實(shí)戰(zhàn),本文介紹了Fiber結(jié)構(gòu),F(xiàn)iber的生成過程,調(diào)和過程,以及 render 和 commit 兩大階段,需要的朋友可以參考下2022-10-10

