- 示例工程
【免费下载链接】prisma-examples
🚀 Ready-to-run Prisma example projects
本篇指南以 prisma-examples 仓库中的 accelerate/nuxtjs-starter 项目为蓝本,讲解如何在 Nuxt.js 应用中通过 Prisma ORM 与 Prisma Accelerate 的组合,构建一个带多种缓存策略的"语录生成器"(Quote Generator)。读完本文,你将掌握 Accelerate 的 TTL、SWR、TTL+SWR 与无缓存四种策略的配置方式、cacheStrategy与withAccelerateInfo()的底层调用链,以及如何从运行指标中判断缓存命中状态,从而在真实项目中权衡性能与数据新鲜度。
项目概览:语录生成器如何工作
该项目是一个可直接运行的 Nuxt.js 应用:页面从预置的名言集中检索最近新增的一条名言,并分别以"开启缓存"和"不开启缓存"两种方式返回,用于直观对比 Accelerate 各缓存策略对数据库压力的影响。其核心流程可以拆解为三个步骤:
- Fetch a quote(取名言):检索最近添加的名言(按
id倒序取第一条),分别在有缓存与无缓存两种路径下执行; - Apply caching strategy(应用缓存策略):根据请求参数决定采用 TTL、SWR、TTL+SWR 或完全不缓存中的哪一种策略;
- Return the quote(返回结果):返回名言本身,同时附带缓存状态、缓存节点区域、命中时间等元数据。
从项目源码结构看,这个"三步走"分别落在三个不同的代码层:API 路由(server/api/quotes.ts)负责解析缓存策略参数,查询层(lib/utils/query.ts)负责携带cacheStrategy执行 Prisma 查询并采集性能信息,视图层(pages/index.vue)负责并行发起四种策略的请求并渲染结果。README 中明确指出,该项目演示了 Accelerate 提供的全部缓存策略,是学习 Accelerate 缓存体系的最小完整范例。
前置条件
在开始之前,你需要准备以下两项:
- 连接串:一个公网可访问数据库的连接串(示例项目使用 PostgreSQL,见下文 Schema 分析);
- Accelerate 连接串:包含Accelerate API Key的 Accelerate 连接串。你需要在 Prisma Data Platform 中为项目启用 Accelerate 后获取。Accelerate 连接串具有如下固定结构:
prisma://accelerate.prisma-data.net/?api_key=__YOUR_ACCELERATE_API_KEY__其中__YOUR_ACCELERATE_API_KEY__是你在 Prisma Data Platform 生成的 API Key。这条连接串会同时承担"数据库代理 + 连接池 + 缓存层"三重职责,Prisma Client 的所有查询都经由它转发。
从零跑起来:完整启动步骤
1. 克隆仓库并安装依赖
克隆 prisma-examples 仓库(可使用--depth=1浅克隆以加速),进入示例目录并安装依赖:
git clone https://gitcode.com/gh_mirrors/pr/prisma-examples.git --depth=1 cd prisma-examples/accelerate/nuxtjs-starter npm install依赖清单以 package.json 为准,核心依赖包括:nuxt3.19.0、@prisma/client7.0.0、@prisma/extension-accelerate2.0.2(Accelerate 客户端扩展)、@prisma/nuxt0.3.0(Nuxt 集成模块)、zod3.25.76(参数校验)以及tsx(用于运行种子脚本)。
2. 配置环境变量
在项目根目录创建.env文件,并写入两个变量:
touch .env# .env # Accelerate connection string (used for queries by Prisma Client) DATABASE_URL="__YOUR_ACCELERATE_CONNECTION_STRING__" NUXT_PUBLIC_URL="http://localhost:3000"DATABASE_URL:填入上一步获取的 Accelerate 连接串(形如prisma://accelerate.prisma-data.net/?api_key=...)。注意__YOUR_ACCELERATE_CONNECTION_STRING__是占位符,必须替换为真实值;NUXT_PUBLIC_URL:前端页面发起请求时使用的服务地址,默认http://localhost:3000。该变量在 lib/utils/helper.ts 中被读取并作为HOST导出,供页面层拼接 API 地址使用。
值得一提的是,示例采用新版 prisma.config.ts 集中管理 Prisma 配置:schema指向prisma/schema.prisma,migrations.path指向迁移目录,seed命令为tsx ./prisma/seed.ts,而datasource.url通过env("DATABASE_URL")读取。这意味着 Schema 中无需再内联数据库地址,配置职责更清晰。
3. 执行迁移建表并写入种子数据
Prisma Schema 中定义了一个Quotes模型。执行以下命令将该模型映射到数据库并创建对应的表:
npx prisma migrate dev --name init执行成功后数据库中将出现一张空的Quotes表。接着运行种子脚本写入示例数据:
npx prisma db seed种子逻辑位于 prisma/seed.ts:使用prisma.quotes.createMany({ skipDuplicates: true })一次性写入 20 条名人名言(如 "The only way to do great work is to love what you do."、"You miss 100% of the shots you don't take." 等),skipDuplicates保证重复执行不会产生重复记录。
4. 生成 Prisma Client
执行生成命令,产出类型安全的客户端代码:
npx prisma generate本项目采用 Prisma 7 的prisma-client生成器,输出目录为./prisma/generated(见 prisma/schema.prisma),生成的客户端随后会被 lib/prisma.ts 与种子脚本引用。
5. 启动开发服务器
npm run dev由于schema与迁移目录都已就绪,启动时会跳过"安装 Prisma CLI""创建 Prisma Schema"等引导提示。此外,nuxt.config.ts 中通过prisma: { autoSetupPrisma: false }关闭了自动初始化,避免重复引导。但 Nuxt DevTools 仍会询问是否安装 Prisma Studio 用于可视化编辑数据:
Do you want to view and edit your data by installing Prisma Studio in Nuxt DevTools? › (Y/n)选择Y即可在 DevTools 中直接浏览与编辑Quotes表。
6. 在浏览器中验证效果
打开浏览器访问http://localhost:3000,即可看到"Accelerated Quotes"页面。页面底部会展示四种缓存策略各自的性能与统计信息(如缓存命中状态 cache/hit):
深入源码:缓存策略在 API 层如何落地
缓存策略的分发入口是 server/api/quotes.ts。该文件基于 Nuxt 3 / h3 的defineEventHandler实现,完整逻辑如下:
import { defineEventHandler, getQuery, createError } from 'h3'; import { z } from 'zod'; import { getQuotes } from '@/lib/utils/query'; export default defineEventHandler(async (event) => { const { cache } = getQuery(event); const parser = z.enum(["TTL", "SWR", "BOTH", "NONE"]); const parsedOutput = await parser.safeParseAsync(cache); if (!parsedOutput.success) { throw createError({ statusCode: 400, statusMessage: "Invalid search parameter." }); } let map = new Map(); map.set("TTL", { ttl: 30 }); map.set("SWR", { swr: 30 }); map.set("BOTH", { ttl: 30, swr: 60 }); map.set("NONE", undefined); const data = await getQuotes(map.get(parsedOutput.data)); return data; });这段代码揭示了几个关键设计:
- 参数白名单校验:
cache查询参数必须严格属于TTL / SWR / BOTH / NONE四选一,使用z.enum配合safeParseAsync做异步校验,非法参数直接返回400 Invalid search parameter,避免误传参数导致意外缓存行为; - 策略到参数对象的映射:四种策略被映射为
cacheStrategy选项——TTL:{ ttl: 30 },30 秒内命中缓存直接返回,缓存未过期前不访问数据库;SWR:{ swr: 30 },Stale-While-Revalidate,过期后先返回旧值再后台刷新,兼顾响应速度与数据更新;BOTH:{ ttl: 30, swr: 60 },TTL 与 SWR 组合,30 秒内强缓存,30~60 秒间以 SWR 方式返回旧数据并异步更新;NONE:undefined,不启用任何缓存,仅使用 Accelerate 的连接池能力,作为对照基线。
- 单一查询函数复用:四种策略共用同一个
getQuotes(),仅在入参上区分,代码零重复。
从查询层 lib/utils/query.ts 可以看到cacheStrategy的完整用法:
export const getQuotes = async (strategy?: CacheStrategy) => { const start = Date.now(); const result = await prisma.quotes .findMany({ cacheStrategy: strategy, orderBy: { id: "desc" }, take: 1, }) .withAccelerateInfo(); return { data: result?.data?.[0], info: result.info, time: Date.now() - start, }; };要点如下:
cacheStrategy作为 Prisma 查询选项直接传给findMany,不传或传undefined时只走连接池、不走缓存,这正是NONE策略的实现方式;.withAccelerateInfo()是 Accelerate 扩展提供的链式方法,查询完成后会追加一个info字段,其中包含缓存状态、缓存节点区域、请求 ID 等诊断信息;- 查询按
id: "desc"倒序并take: 1,语义为"最近新增的一条名言"; - 返回值同时携带
data(名言本体)、info(Accelerate 元数据)与time(从发起到返回的耗时毫秒数),time供页面层直观展示性能对比。
缓存策略与信息类型的完整定义
lib/types/index.ts 给出了这套机制的类型化契约,是理解全链路的关键:
export type CacheStrategy = | { ttl: number; swr: number } | { ttl: number } | { swr: number }; export type AccelerateInfo = { cacheStatus: "ttl" | "swr" | "miss" | "none"; lastModified: Date; region: string; requestId: string; signature: string; }; export type Quote = { id: number; quote: string; createdAt: string; }; export type QuoteResult = { data: Quote; info: AccelerateInfo; time: number; }; export type QuoteCacheType = "SWR" | "TTL" | "No caching" | "TTL + SWR";其中AccelerateInfo.cacheStatus是判断缓存行为的核心指标,取值含义如下:
| cacheStatus | 含义 | 是否命中缓存 |
|---|---|---|
ttl | 命中 TTL 缓存,直接返回缓存数据 | 是(CACHE HIT) |
swr | 命中 SWR 缓存,返回旧数据并触发后台刷新 | 是(CACHE HIT) |
miss | 缓存未命中,回源数据库查询 | 否 |
none | 未启用缓存(即NONE策略) | 否 |
lastModified表示缓存数据的最后修改时间,region表示处理请求的 Accelerate 缓存节点区域,requestId可用于跨端排查单次请求。
客户端如何构建与缓存诊断信息对接
lib/prisma.ts 负责创建经过 Accelerate 扩展的 Prisma Client 单例:
import { PrismaClient } from '../prisma/generated/client'; import { withAccelerate } from '@prisma/extension-accelerate' const prismaClientSingleton = () => { return new PrismaClient({ accelerateUrl: process.env.DATABASE_URL, }).$extends(withAccelerate()) } const prisma = globalThis.prismaGlobal ?? prismaClientSingleton() export default prisma if (process.env.NODE_ENV !== 'production') globalThis.prismaGlobal = prisma这里有两个值得注意的实现细节:
accelerateUrl注入:将.env中的DATABASE_URL(即 Accelerate 连接串)作为accelerateUrl传入PrismaClient构造器,再通过$extends(withAccelerate())挂载 Accelerate 能力。这也解释了为何 prisma/schema.prisma 的datasource中只有provider = "postgresql"而没有url——连接地址统一由prisma.config.ts与运行时环境变量管理;- 开发环境热更新单例:沿用 Next.js 社区经典的
globalThis缓存模式,在非生产环境下将实例挂到globalThis,避免 Nuxt 开发模式热更新时反复创建 Prisma Client 导致连接数膨胀。种子脚本 prisma/seed.ts 使用了相同的new PrismaClient({ accelerateUrl }).$extends(withAccelerate())写法。
前端视图:四种策略同屏对比
页面层 pages/index.vue 使用 Nuxt 内置的useFetch并行发起四个请求,恰好对应四种缓存策略:
const { data: ttl } = useFetch<QuoteResult>(() => `${HOST}/api/quotes?cache=TTL`); const { data: swr } = useFetch<QuoteResult>(() => `${HOST}/api/quotes?cache=SWR`); const { data: both } = useFetch<QuoteResult>(() => `${HOST}/api/quotes?cache=BOTH`); const { data: none } = useFetch<QuoteResult>(() => `${HOST}/api/quotes?cache=NONE`);四个卡片分别渲染"TTL""SWR""TTL + SWR""No caching"的结果。卡片组件 components/Quote.vue 是缓存诊断信息的前端呈现层,其判定逻辑直接复用了cacheStatus的语义:
const cacheStatus = computed(() => props.result?.info?.cacheStatus || '');<span class="font-bold" :class="{ 'text-green-400': cacheStatus === 'swr' || cacheStatus === 'ttl', 'text-red-400': cacheStatus !== 'swr' && cacheStatus !== 'ttl' }"> {{ cacheStatus?.toUpperCase() }} {{ cacheStatus === 'swr' || cacheStatus === 'ttl' ? ' CACHE HIT' : '' }} </span>即:当cacheStatus为swr或ttl时以绿色显示并标注CACHE HIT,其余(miss/none)以红色显示。除此之外,卡片还展示以下指标:
- Cache Node Region:处理本次请求的 Accelerate 缓存节点区域(来自
info.region),可帮助确认请求是否被就近的缓存节点承接; - Cached Modified at:缓存数据的最后修改时间(来自
info.lastModified); - Cache status:上述
cacheStatus的命中判定; - Time taken:单次请求耗时(
result.time,毫秒),用于直观对比缓存与直连数据库的延迟差异。
外层布局由 app.vue 提供:"Accelerated Quotes" 标题、说明文案以及一个Refresh按钮(内部实现为window.location.reload()),方便反复刷新观察缓存命中状态随时间推移的变化——例如 TTL 30 秒过期后再次刷新,cacheStatus会从ttl变为miss,耗时随之上升。
运行效果解读与上手建议
启动后反复刷新页面,可以观察到一个清晰的对照实验:NONE卡片每次请求都回源数据库,耗时相对稳定;TTL卡片在 30 秒内刷新会命中缓存(绿色TTL CACHE HIT),超时后首次刷新变为miss;SWR卡片在旧数据过期后仍能立即返回数据并后台刷新;BOTH卡片则在前 30 秒强缓存、30~60 秒间 SWR 的窗口内展示组合策略的行为。若配合 Prisma Studio 修改某条名言后立即刷新,还能直观验证"缓存期间数据不实时"这一特性。
针对不同业务场景,可以给出如下选型建议:
- 数据几乎不变化、对延迟极度敏感的场景,优先使用
TTL; - 允许短暂展示旧数据、希望响应始终快速的场景,使用
SWR; - 需要兼顾强缓存与最终一致性的场景,使用
TTL + SWR组合; - 数据实时性要求极高、或仅需连接池聚合时,使用
NONE(cacheStrategy传undefined)。
此外,本仓库的 accelerate/nextjs-starter(其 lib/utils/query.ts 还额外提供了addQuote写入示例)、accelerate/astro-starter 等兄弟项目共享同一套withAccelerate+withAccelerateInfo用法,可作为跨框架迁移的参考。更底层的cacheStrategy选项完整说明,可查阅 Prisma Accelerate 官方缓存文档。
总结
通过这个 Nuxt.js Starter,可以一次性看清 Prisma Accelerate 缓存体系的完整链路:?cache=查询参数经 zod 白名单校验后,被映射为cacheStrategy查询选项;查询通过$extends(withAccelerate())扩展后的客户端执行,并由.withAccelerateInfo()带回cacheStatus、region、lastModified等诊断元数据;最终由前端卡片组件将ttl/swr/miss/none四种状态渲染为可视化的命中指标。对任何希望在生产项目中引入 Prisma Accelerate 缓存、又需要可量化对比收益的团队而言,这份示例都是值得直接复用的起点。
- 示例工程
【免费下载链接】prisma-examples
🚀 Ready-to-run Prisma example projects
相关推荐
Prisma Accelerate终极指南:如何通过缓存策略提升应用性能300%
Prisma Accelerate终极指南:如何通过缓存策略提升应用性能300% 🚀 想要让你的数据库查询速度提升3倍以上吗?Prisma Accelerat
示例工程MediaGo 前端实践:用显式条件渲染杜绝 `0` 与 `NaN` 泄漏进 JSX
MediaGo 前端实践:用显式条件渲染杜绝 0 与 NaN 泄漏进 JSX 导读 在 React / TSX 组件开发中, {count && <span {
示例工程Prisma Accelerate 深度应用:连接池与全局缓存优化
Prisma Accelerate 深度应用:连接池与全局缓存优化 本文深入探讨了Prisma Accelerate的核心功能,包括连接池配置与管理、全局缓存策
示例工程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考