
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_PASSWORDACCESS_HOST_URL/DATABASE_NAME?sslacceptstrict?sslacceptstrict固定追加含义是强制 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 prismaPlanetScale 默认没有外键约束关系完整性改由 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服务端直接findManyapp/layout.tsx 声明了 PlanetScale MySQL Next.js 的页面元数据。这里藏着一个新手容易踩的点在 lib/prisma.tsconst 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一组新的让开发、生产凭据独立、可单独轮换。部署本身只有三步在 Vercel 导入nextjs-mysql仓库环境变量里添加DATABASE_URL值填前文格式拼好的生产连接串完成。页面全部服务端渲染构建期不碰数据库每次请求由服务端直查 PlanetScale。两个提醒生产上别沿用仅限本地的分支凭据连接串保留?sslacceptstrict。排障按现象 → 原因 → 处理各一条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),仅供参考