☰
Next.js 接入 PlanetScale MySQL:用 with-mysql 示例把电商页跑通真实数据
2026/10/5 2:24:53 网站建设 项目流程

Next.js 接入 PlanetScale MySQL:用 with-mysql 示例把电商页跑通真实数据

【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js

想让 Next.js 接入 PlanetScale MySQL,往往卡在两处:连接串拼不对,开发时热更新又把 Prisma 客户端反复重建,连接数很快超限。with-mysql 示例就是干这件事的:一个 App Router + Prisma + PlanetScale + Tailwind CSS 的全栈启动模板,把建库、凭据、Schema、种子数据到部署的链路装进一个可运行的商品列表。下面按数据流向把这个骨架拆开讲。

一条数据看全貌:商品行如何从数据库流到浏览器

先别看依赖清单,顺着数据走更直观。

数据落在 PlanetScale——基于 Vitess 的托管 MySQL 服务。运行时真正搬查询的是@prisma/adapter-planetscale:这个 Driver Adapter(负责把 Prisma 查询转发到数据库的适配层)并不直说 MySQL 协议,而是把 SQL 走 HTTPS 发到 PlanetScale 的 Serverless Driver 网关,依赖里的undici就是为它提供 fetch 实现的。适配层之上是 Prisma 生成的类型安全客户端;应用层里 app/page.tsx 作为 async Server Component 在服务端直接prisma.product.findMany({ include: { category: true } }),一次联表取出商品和分类;最后 components/Product.tsx 把每行渲染成卡片,Tailwind CSS v4 负责外观。全程没有单独的 API 层,查询在服务端完成,浏览器只收到渲染好的 HTML。

各文件的分工拆开看:

路径职责为什么存在
lib/prisma.tsPrismaClient 单例,实例化 PlanetScale 适配器热更新时复用连接,避免耗尽连接数
prisma/schema.prismaProduct/Category 模型与生成器配置决定表结构和客户端产出路径
prisma/seed.ts种子脚本幂等重建,可重复执行
prisma/data.ts3 个分类、4 件商品的种子数据页面演示数据
prisma.config.tsPrisma CLI 配置给命令提供连接串、指向种子脚本
.env.exampleDATABASE_URL模板CLI 与运行时共读同一变量
app/page.tsx服务端取数数据流的起点
components/Product.tsx商品卡片渲染单行数据
package.json依赖与prisma.seed定义种子命令的执行方式

动手前:先懂分支,再建库,再拉项目

PlanetScale 的基本单元是分支(branch)。建库时会自动生成一个main分支,开发分支让你在不碰生产分支的前提下随便改 schema 和数据,再通过 deploy request(把开发分支合并回主分支的合并请求)合回去。正因为有这个设计,后面"把分支提升为生产分支"这一步才顺理成章。

命令行入口统一是pscale。装好 PlanetScale CLI 后先完成认证:

pscale auth login

浏览器里走完 OAuth,凭据存在本地供后续命令使用。然后建一个 MySQL 引擎的库:

pscale database create <DATABASE_NAME> --engine mysql

建完即可把main直接当作下文命令里的BRANCH_NAME。

项目本身用官方模板拉取,不用手装依赖:

npx create-next-app --example with-mysql nextjs-mysql

换包管理器对应改成:

yarn create next-app --example with-mysql nextjs-mysql pnpm create next-app --example with-mysql nextjs-mysql bunx create-next-app --example with-mysql nextjs-mysql

命令会在nextjs-mysql目录里装齐 package.json 声明的全部依赖。依赖不多但各司其职:运行时是next/react/react-dom、@prisma/client、@prisma/adapter-planetscale、undici;开发侧是prisma、tsx(跑种子脚本)、dotenv、TypeScript 相关,以及 Tailwind v4 与@tailwindcss/postcss。

打通数据库连接:先生成凭据拼出连接串,再读 Schema

先把环境模板变成真实文件:

mv .env.example .env

再用 CLI 给分支创建一组凭据。PASSWORD_NAME只是给这组凭据起的名字——同一分支可以挂多组凭据,靠它区分本地开发、生产,也方便单独轮换:

pscale password create <DATABASE_NAME> <BRANCH_NAME> <PASSWORD_NAME>

返回的明文密码只展示这一次,立刻存好:

Password <PASSWORD_NAME> was successfully created in <DIRECTORY_NAME>. Please save the values below as they will not be shown again NAME USERNAME ACCESS HOST URL ROLE PLAIN TEXT ---------------- -------------- ----------------------------- ----------------- ----------------------------- <PASSWORD_NAME> xxxxxxxxxxxxx xxxxxx.us-east-2.psdb.cloud Can Read & Write pscale_pw_xxxxxxx

把返回的四个字段拼成标准 MySQL 连接串,作为.env里DATABASE_URL的值:

mysql://<USERNAME>:<PLAIN_TEXT_PASSWORD>@<ACCESS_HOST_URL>/<DATABASE_NAME>?sslaccept=strict

?sslaccept=strict固定追加,含义是强制 TLS 加密连接。这个变量有两处消费方:CLI 侧由 prisma.config.ts 经env("DATABASE_URL")读取;运行时侧由 lib/prisma.ts 从process.env.DATABASE_URL读取。两者都不在 Next.js 的请求上下文里,所以这两个文件顶部都显式import "dotenv/config",确保根目录的.env被加载。

Schema 里哪些字段决定了连接串怎么用

prisma/schema.prisma 全文不长:

generator client { provider = "prisma-client" output = "../lib/generated/prisma" } datasource db { provider = "mysql" relationMode = "prisma" } model Product { id Int @id @default(autoincrement()) name String description String price Decimal image String category Category @relation(fields: [categoryId], references: [id]) categoryId Int @@index([categoryId]) } model Category { id Int @id @default(autoincrement()) name String description String products Product[] }

几个地方值得追问一句"为什么这么写":

  • 为什么provider = "prisma-client"还带output?这是 Prisma 新版 TypeScript 生成器的写法:客户端代码输出到lib/generated/prisma而不是传统的node_modules/.prisma,所以应用和种子脚本都从"@/lib/generated/prisma/client"这个本地路径导入类型与客户端。
  • 为什么 datasource 块里没有url?连接串统一由prisma.config.ts提供,prisma db push、prisma generate等 CLI 命令执行时读取该配置。
  • 为什么relationMode = "prisma"?PlanetScale 默认没有外键约束,关系完整性改由 Prisma 在应用层维护。Product通过categoryId引用Category,该列上补了@@index加速按分类查询。
  • 为什么运行时要 adapter 而不是直连 TCP?因为真正建立连接的就是适配器:它接收连接串后把查询经 HTTPS 转发给 Serverless Driver 网关,客户端不再说 MySQL 协议,undici的 fetch 因此被传给它。

price用Decimal是为了避开货币字段的浮点误差,种子数据同样用new Prisma.Decimal(...)构造精确值。另外提醒:本示例面向 PlanetScale 的 MySQL 兼容服务,如果你用的是 PlanetScale Postgres,驱动适配器和连接串格式都不同,不能混用。

让数据跑起来:generate、push、seed,然后本地运行

三条命令按顺序执行:

npx prisma generate

这一步按generator client的output路径产出类型安全客户端,TS 从此认识@/lib/generated/prisma/client。

npx prisma db push

不生成迁移文件,把当前 Schema 直接推进数据库建表,适合原型阶段。

npx prisma db seed

实际执行tsx prisma/seed.ts(命令定义在 package.json 的prisma.seed字段,与 prisma.config.ts 的migrations.seed保持一致)。

种子脚本的策略是幂等重建——可以重复执行、结果不变:先deleteMany()清空两张表,再用$executeRaw把AUTO_INCREMENT归零到 1,最后createMany批量写入 data.ts 里的 3 个分类(Hats / Socks / Shirts)和 4 件商品。因果关系在这里:商品用categoryId: 1/2/3这种固定值引用分类主键,只有重置自增,先插入的分类才真正落在 1/2/3 上,外键引用才永远成立。

启动开发服务器:

npm run dev

浏览器打开 http://localhost:3000,就能看到从数据库渲染出来的商品网格 😄

取数入口 app/page.tsx 是 async Server Component,服务端直接findMany;app/layout.tsx 声明了 "PlanetScale MySQL + Next.js" 的页面元数据。

这里藏着一个新手容易踩的点,在 lib/prisma.ts:

const globalForPrisma = globalThis as unknown as { prisma: PrismaClient | undefined; }; function createPrismaClient() { const adapter = new PrismaPlanetScale({ url: process.env.DATABASE_URL, fetch: undiciFetch, }); return new PrismaClient({ adapter }); } const prisma = globalForPrisma.prisma ?? createPrismaClient(); if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma; export default prisma;

为什么绕这一圈?开发模式下模块会随文件变更反复重执行,每次重执行都new PrismaClient()的话,旧连接不释放又开新连接,很快触发连接数上限。所以把实例挂到globalThis上,??保证只创建一次,热更新后复用旧实例;生产环境模块只加载一次,于是仅在非生产环境才挂全局。

上生产:promote 分支,再配好 Vercel 环境变量

PlanetScale 里main默认是开发角色,只有被提升(promote)后才能承接生产流量:

pscale branch promote <DATABASE_NAME> <BRANCH_NAME>

提升之后,凭据有两个选择:直接沿用本地开发用的那组,或再pscale password create一组新的,让开发、生产凭据独立、可单独轮换。

部署本身只有三步:

  1. 在 Vercel 导入nextjs-mysql仓库;
  2. 环境变量里添加DATABASE_URL,值填前文格式拼好的生产连接串;
  3. 完成。页面全部服务端渲染,构建期不碰数据库,每次请求由服务端直查 PlanetScale。

两个提醒:生产上别沿用仅限本地的分支凭据;连接串保留?sslaccept=strict。

排障按"现象 → 原因 → 处理"各一条:

  • PrismaClientInitializationError/ 连接失败→ 连接串四个占位符有没替换的,或域名带了多余路径 → 对照 CLI 返回逐项核对。
  • 表建了但没数据→db push和db seed有漏跑 → 按顺序补跑,种子幂等不怕重复。
  • 种子脚本读不到环境变量→ 命令没在项目根目录执行,dotenv/config没加载到.env→ 回到根目录重跑。
  • 开发期连接数超限→ 有代码绕过 lib/prisma.ts 单例直接new PrismaClient()→ 统一走单例导出。

这套骨架替换掉数据模型与种子数据,就能原样搬进真实业务:改 schema 里的两个 model、换掉 data.ts 的数组,就得到你自己的 Next.js + PlanetScale + Prisma 版本。

【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询