从零到上线:Next.js 接入 PlanetScale MySQL 的完整实战指南
【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js
Next.js 官方仓库的with-mysql示例,把 App Router、Prisma ORM 与托管数据库 PlanetScale MySQL 装进一个可运行的电商商品列表里。跟着走一遍,你能拿到一套"云端建库 → 本地跑通 → 部署上线"的完整 MySQL 接入流程,而且每个环节都知道它为什么这么设计。
先看地图:谁负责什么
这套示例的分工非常克制,每个文件只干一件事:
| 路径 | 职责 |
|---|---|
| prisma/schema.prisma | 定义 Product / Category 模型与客户端生成器 |
| prisma/data.ts | 3 个分类、4 件商品的种子数据 |
| prisma/seed.ts | 清空旧数据并批量灌入种子 |
| prisma.config.ts | 为 Prisma CLI 提供 schema 路径与连接串来源 |
| lib/prisma.ts | 运行时数据库客户端(全局单例封装) |
| app/page.tsx | 服务端组件,直接查库渲染商品网格 |
| components/Product.tsx | 单张商品卡片 |
| .env.example | DATABASE_URL连接串模板 |
技术组合是 Next.js(App Router)+ Prisma(新版 TypeScript 客户端)+@prisma/adapter-planetscale驱动适配器 + Tailwind CSS v4。适配器把 SQL 通过 HTTPS 发往 PlanetScale 网关,所以运行时依赖里才有undici——它给适配器提供 fetch 实现,客户端本身不走 MySQL 协议。
认证 CLI 并建库:把引擎类型和默认分支定下来
🔑 动手前先装好 PlanetScale CLI,用pscale auth login完成浏览器授权,之后所有命令都靠这份本地凭据。
建库命令很短:
pscale database create <DATABASE_NAME> --engine mysql输出里会告诉你数据库创建成功。值得注意的隐藏事实是:建库同时自动生成了一个名为main的分支,PlanetScale 的 schema 变更都发生在分支上,main就是后续所有操作的起点。引擎类型必须在建库时用--engine mysql钉死,Postgres 是另一套模板,两者的适配器和连接串格式完全不通用,事后换不掉。
拉下模板工程:一条命令装齐全部依赖
凭据有了,接下来把代码拉下来:
npx create-next-app --example with-mysql nextjs-mysqlyarn / pnpm / bun 也有对应写法。执行完会在nextjs-mysql目录里装好 package.json 声明的全部依赖:运行时是next、react、@prisma/client、@prisma/adapter-planetscale、undici;开发侧是prismaCLI、tsx(跑种子脚本)、dotenv(加载.env)和 Tailwind v4。依赖看着不多,但每个都有明确职责,这也是后面排查问题时的排查清单。
生成凭据拼连接串:明文密码只展示一次
先复制模板文件:
mv .env.example .env然后给main分支创建一组凭据:
pscale password create <DATABASE_NAME> main <PASSWORD_NAME>输出会长这样——明文密码只展示这一次,务必当场保存:
NAME USERNAME ACCESS HOST URL ROLE PLAIN TEXT <PASSWORD_NAME> xxxxxxxxxxxxx xxxxxx.us-east-2.psdb.cloud Can Read & Write pscale_pw_xxxxxxx把四个字段拼进.env的DATABASE_URL:
| 参数 | 来源 | 说明 |
|---|---|---|
<USERNAME> | 输出的USERNAME | 数据库用户名 |
<PLAIN_TEXT_PASSWORD> | 输出的PLAIN TEXT | 只出现一次的明文密码 |
<ACCESS_HOST_URL> | 输出的ACCESS HOST URL | 访问域名,末尾别加斜杠 |
<DATABASE_NAME> | 建库时的名称 | 目标库名 |
?sslaccept=strict | 固定追加 | 强制 TLS |
同一分支允许多组凭据,PASSWORD_NAME就是给这组凭据贴的用途标签——开发一组、生产一组,轮换时互不影响。这条连接串会被两边消费:Prisma CLI 从 prisma.config.ts 的env("DATABASE_URL")读,应用运行时从process.env.DATABASE_URL读,所以格式拼错一次,两边同时报错。
看懂数据模型:连接串为什么不写进 schema
打开 prisma/schema.prisma 会看到两个模型和一处刻意的留白:
generator client { provider = "prisma-client" output = "../lib/generated/prisma" }- 客户端输出到
lib/generated/prisma而不是传统的node_modules/.prisma,因此应用和种子脚本都从@/lib/generated/prisma/client导入类型,生成产物跟着代码库走; datasource块里没有url,连接串统一由 prisma.config.ts 提供。这是新版 Prisma 的推荐做法:schema 只描述"结构",环境相关的信息集中到一处,改库名不用动 schema;relationMode = "prisma"表示外键完整性由应用层维护,适配 PlanetScale 不允许传统外键约束的默认场景;price用Decimal而不是浮点数,种子数据里配合new Prisma.Decimal(19.95)构造,避免货币精度丢失。
模型本身是一对多:Product通过categoryId引用Category并加了@@index([categoryId]),按分类筛选商品时走索引。
推模型灌种子:三条命令把空库变成商品库
凭据配好后按顺序执行:
npx prisma generate # 按 schema 生成类型安全的客户端 npx prisma db push # 把模型直接同步成数据库表结构 npx prisma db seed # 写入演示数据db push不生成迁移文件,适合原型阶段;想留迁移历史就换成migrate系列命令。db seed实际执行的是 package.json 里prisma.seed声明的tsx prisma/seed.ts。
种子脚本的策略是幂等重建:先deleteMany()清空两张表,再用$executeRaw把AUTO_INCREMENT重置为 1,然后createMany批量写入 data.ts 里的 3 个分类和 4 件商品。重置自增是必要的——商品通过categoryId: 1/2/3硬关联分类主键,不重置的话第二次灌数据时外键就会指向不存在的 id。脚本重复执行永远安全,这对你调试期间反复灌库非常友好。
跑通本地页面:单例防止连接爆炸
npm run dev访问http://localhost:3000,能看到渲染出来的商品网格:
数据流向是 app/page.tsx 这个 async 服务端组件直接执行prisma.product.findMany({ include: { category: true } }),一次联表取出商品和所属分类,再逐项传给Product卡片组件。没有中间的 API 层,客户端只收到 HTML——这是 App Router 推荐的取数姿势。
真正值得逐行看的是 lib/prisma.ts:
const prisma = globalForPrisma.prisma ?? createPrismaClient(); if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;为什么非要挂在globalThis上?因为 Next.js 开发模式下,每次文件变更都会重新执行模块代码,如果每轮都new PrismaClient(),相当于每次保存都新开一条数据库连接,连接数很快顶到上限。把实例挂到全局后,热更新只创建一次,后续全部复用;生产环境模块只加载一次,所以只在非生产环境才写全局。
部署前提升分支:生产流量只走被提升的 main
🚀 本地跑通后,上线前还差一个数据库动作:
pscale branch promote <DATABASE_NAME> main在 PlanetScale 里,main分支默认是开发角色,只有被提升为生产分支后,生产流量才有资格访问它。不提升就直接部署,线上请求会因为没有可用的生产凭据路径而失败。提升后,你可以复用之前那组密码,也可以新跑一次pscale password create给生产单独配一组——推荐后者,开发和生产凭据隔离,出问题时能立刻轮换生产而不动本地。
最后是部署平台侧:把项目导入部署平台,在环境变量里填上生产DATABASE_URL即可。由于页面全是服务端渲染,构建期不需要连库,每次请求时实时查 PlanetScale。
排错清单:现象、原因与处置
🧯 按出现频率排序:
| 现象 | 原因 | 处置 |
|---|---|---|
PrismaClientInitializationError/ 连接失败 | 连接串四个字段没替换全,或域名末尾多了路径 | 对照 CLI 输出逐字段核对,保留?sslaccept=strict |
| 页面是空的,没有商品 | 只跑了generate,没跑db push和db seed | 补跑后两条命令,种子可重复执行 |
种子脚本报找不到DATABASE_URL | dotenv/config依赖当前目录是项目根 | 在nextjs-mysql根目录执行命令 |
| 开发期连接数超限 | 绕过单例直接new PrismaClient() | 统一从 lib/prisma.ts 导入 |
| 线上 401 或连不上库 | 分支没提升,或用了开发凭据访问生产 | pscale branch promote并换生产连接串 |
提示:连接串的四个字段都来自
pscale password create的同一次输出,缺一个都连不上;保存密码的那步输出是唯一窗口,丢了只能重新password create一组新凭据。
如何迁移到自己的项目
这套骨架可以直接当模板用,替换点集中在三处:
- 模型:改 prisma/schema.prisma 为你的业务表,保留
prisma-client生成器与prisma.config.ts的连接串外置写法,重新prisma generate+db push; - 种子数据:把你的初始数据放进
prisma/data.ts,照抄 seed.ts 的"清空 → 重置自增 → 批量写入"流程,保证可重复执行; - 取数与渲染:在服务端组件里 import lib/prisma.ts 导出的单例实例做查询,不要自己
new客户端。
环境差异只在凭据:本地一组、生产一组,连接串格式不变。把这三层解耦记牢,之后换数据库供应商时你只需要换适配器和连接串,业务代码一行不动。
【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考