- 示例工程
【免费下载链接】prisma-examples
🚀 Ready-to-run Prisma example projects
本文基于 prisma-examples 仓库中的 accelerate/svelte-starter 项目,讲解如何在 SvelteKit 应用中接入 Prisma ORM 与 Prisma Accelerate,用最小的代码闭环演示四种缓存策略(TTL、SWR、TTL+SWR 与不缓存)的配置、请求处理与命中观测。读完你将掌握 Accelerate 连接串的配置方式、cacheStrategy参数的取值与语义、基于withAccelerateInfo()的缓存元数据解读,以及一条从建表、灌数据到启动页面的完整可运行路径。
项目定位:一个展示 Accelerate 全缓存策略的名言生成器
accelerate/svelte-starter的核心是一个名言生成器(quote generator):它从预置的名言数据集中检索最新添加的一条名言,分别以"启用缓存"和"不启用缓存"两种方式返回,从而让开发者直观对比不同缓存策略下的表现。项目在 SvelteKit 应用中同时使用了 Prisma ORM 与 Prisma Accelerate,并在 API 路由 中演示了 Accelerate 提供的每一种缓存策略。
名言生成器的工作流程可以拆解为三个环节:
- 获取名言(Fetch a quote):检索最近添加的名言,既支持开启缓存,也支持关闭缓存;
- 应用缓存策略(Apply caching strategy):根据请求参数选择不同的缓存策略,包括 TTL、SWR、TTL+SWR,或者完全不用缓存;
- 返回名言(Return the quote):返回选中的名言,同时附带缓存状态等元数据(cache status、命中区域、耗时等)。
从源码结构看,这三个环节分别落在 API 路由(策略解析与分发)、查询封装(执行查询并附带 Accelerate 信息)以及 前端组件(元数据可视化)中。
运行前置条件
在动手运行之前,需要准备两样东西:
- 数据库连接串(connection string):一个公网可访问数据库的连接串,用于建表和写入种子数据;
- Accelerate 连接串:包含你的Accelerate API key,可以在 Prisma Data Platform 中为项目启用 Accelerate 后获取。Accelerate 连接串的结构为:
prisma://accelerate.prisma-data.net/?api_key=__YOUR_ACCELERATE_API_KEY__Accelerate 连接串负责在 Prisma Client 与底层数据库之间建立一条加速通道,Prisma Client 的所有查询都经由它执行,而缓存策略正是由 Accelerate 在这条通道上实现的。
完整上手指南:五步跑通示例
1. 克隆仓库并安装依赖
从镜像仓库克隆项目(--depth=1只拉取最新一次提交,加快克隆速度),进入示例目录并安装依赖:
git clone https://gitcode.com/gh_mirrors/pr/prisma-examples.git --depth=1 cd prisma-examples/accelerate/svelte-starter npm install项目依赖由 package.json 管理,其中与本文主题直接相关的依赖包括@prisma/client、@prisma/extension-accelerate(Accelerate 扩展)、prisma(CLI),以及用于请求参数校验的zod和用于日期格式化的date-fns。
2. 配置环境变量
在项目根目录创建.env文件:
touch .env然后写入以下内容:
# .env # Accelerate connection string (used for queries by Prisma Client) DATABASE_URL="__YOUR_ACCELERATE_CONNECTION_STRING__" VITE_PUBLIC_URL="http://localhost:5173"其中__YOUR_ACCELERATE_CONNECTION_STRING__是占位符,需要替换为你自己的 Accelerate 连接串(结构为prisma://accelerate.prisma-data.net/?api_key=__YOUR_ACCELERATE_API_KEY__)。VITE_PUBLIC_URL用于前端浏览器环境访问 API,默认指向 Vite 开发服务器地址http://localhost:5173。
值得说明的是,DATABASE_URL在这一项目中有两处消费方:一是 prisma.config.ts 通过env("DATABASE_URL")将其作为datasource的 URL 提供给 Prisma CLI(迁移、种子等命令使用);二是 Prisma 客户端初始化 将其作为accelerateUrl传入客户端(应用运行时查询使用)。
3. 迁移建表并写入种子数据
Prisma schema 中只定义了一个Quotes模型,可以用以下命令把它映射到数据库并创建对应的Quotes表:
npx prisma migrate dev --name init迁移完成后,数据库里会有一张空的Quotes表。接着运行种子脚本,向表中写入示例名言记录:
npx prisma db seed种子脚本的入口在 prisma.config.ts 中声明为tsx ./prisma/seed.ts,实际执行逻辑位于 prisma/seed.ts:通过createMany批量写入 20 条经典名言,并开启skipDuplicates: true避免重复数据。
4. 生成 Prisma Client
执行以下命令生成 Prisma Client:
npx prisma generateschema.prisma 中指定了生成器配置:provider = "prisma-client"、output = "./generated",因此生成产物位于prisma/generated目录,代码中通过prisma/generated/client导入。
5. 启动应用
npm run dev启动后访问http://localhost:5173,即可看到"带缓存"与"不带缓存"两种方式下检索到的最近名言,以及对应的缓存统计信息。
从源码理解 Accelerate 的接入方式
数据模型:单表 Quotes
schema.prisma 中只有一个模型:
model Quotes { id Int @id @default(autoincrement()) quote String createdAt DateTime @default(now()) }它只有三个字段:自增主键id、名言正文quote和默认取当前时间的createdAt。示例中查询"最新添加的名言"正是通过orderBy: { id: "desc" }结合take: 1实现的。
客户端初始化:accelerateUrl + withAccelerate
Prisma Client 的初始化集中在 src/lib/prisma.ts:
import { PrismaClient } from '../../prisma/generated/client' import { withAccelerate } from '@prisma/extension-accelerate' const prisma = new PrismaClient({ accelerateUrl: process.env.DATABASE_URL, }).$extends(withAccelerate()); export default prisma关键点有两个:
accelerateUrl:把.env中的DATABASE_URL(即 Accelerate 连接串)传给 PrismaClient,使所有查询经由 Accelerate 转发;$extends(withAccelerate()):通过扩展机制为客户端注入cacheStrategy查询参数和withAccelerateInfo()方法——前者用于声明缓存策略,后者用于取回缓存的元数据。
这一单例客户端被 query.ts 复用,保证了整个应用共享同一个带 Accelerate 扩展的连接实例。
种子数据与迁移配置
prisma.config.ts 完整承担了 Prisma 7 时代的配置职责:
export default defineConfig({ schema: "prisma/schema.prisma", migrations: { path: "prisma/migrations", seed: "tsx ./prisma/seed.ts", }, datasource: { url: env("DATABASE_URL"), }, });migrations.path指定迁移文件目录;migrations.seed指定npx prisma db seed实际执行的命令;datasource.url从环境变量读取数据库连接串,供migrate等 CLI 命令使用。
四种缓存策略的实现与参数
这是整个示例的核心。API 路由 src/routes/api/quotes/+server.ts 用一张映射表集中定义了四种策略:
const cacheStrategies: Record<QuoteCacheType, CacheStrategy | undefined> = { 'SWR': { swr: 30 }, 'TTL': { ttl: 30 }, 'No caching': undefined, 'TTL + SWR': { ttl: 30, swr: 60 }, };对应的类型定义在 src/lib/types/index.ts:
export type CacheStrategy = | { ttl: number; swr: number } | { ttl: number } | { swr: number };四种策略的含义与参数取值如下:
策略(cache参数) | cacheStrategy取值 | 行为说明 |
|---|---|---|
TTL | { ttl: 30 } | 数据在缓存中存活 30 秒;存活期内命中直接返回缓存,减少数据库压力 |
SWR | { swr: 30 } | Stale-While-Revalidate:数据过期后的 30 秒内仍可返回旧数据,同时在后台异步刷新缓存 |
TTL + SWR | { ttl: 30, swr: 60 } | 前 30 秒为新鲜缓存,之后 60 秒内允许返回过期数据并后台重新验证 |
No caching | undefined | 不启用任何缓存,仅使用 Accelerate 的连接池能力 |
其中undefined的策略在 query.ts 的注释中也有明确说明:"cacheStrategycan also be undefined, which would mean only connection pooling is being used"(不传缓存策略即只使用连接池)。
请求参数的解析与校验
客户端通过 URL 查询参数?cache=TTL(或SWR、TTL + SWR、No caching)来选择策略。服务端在 GET 处理器 中完成解析与校验:
const cache = decodeURIComponent(url.searchParams.get('cache') || '') as QuoteCacheType; const parser = z.enum(["TTL", "SWR", "TTL + SWR", "No caching"]); const parsedOutput = await parser.safeParseAsync(cache); if (!parsedOutput.success) { return json({ error: 'Invalid search parameter.' }, { status: 400 }); }这里用zod的enum校验器对参数做白名单校验:不在四种取值之内的参数会得到400错误响应。校验通过后,从映射表中取出对应的CacheStrategy,交给查询封装执行。
查询封装:cacheStrategy 与 withAccelerateInfo
实际的查询逻辑封装在 src/lib/utils/query.ts:
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: strategy:把映射表算出的策略对象直接传给查询,Accelerate 据此决定缓存行为;.withAccelerateInfo():查询链式调用该方法后,返回结果会附带info元数据;- 耗时统计:用
Date.now()记录查询前后时间差,前端据此展示每次请求的实际耗时。
返回的info对应 AccelerateInfo 类型:
export type AccelerateInfo = { cacheStatus: "ttl" | "swr" | "miss" | "none"; lastModified: Date; region: string; requestId: string; signature: string; };字段语义如下:
| 字段 | 说明 |
|---|---|
cacheStatus | 缓存状态:ttl(TTL 命中)、swr(SWR 命中)、miss(未命中)、none(未启用缓存) |
lastModified | 缓存内容最近一次修改的时间 |
region | 响应来自哪个 Accelerate 缓存节点区域 |
requestId | 本次请求的标识 |
signature | 请求签名 |
前端页面如何消费缓存接口
页面入口 src/routes/+page.svelte 在挂载时并行发起四次请求,分别对应四种缓存策略:
onMount(async () => { try { ttl = await fetchQuotes('TTL') swr = await fetchQuotes('SWR') both = await fetchQuotes('TTL + SWR') none = await fetchQuotes('No caching') } catch (error) { ... } })四个结果通过网格布局(grid-cols-4)渲染为四张并排卡片,由 Quote.svelte 负责展示。每张卡片呈现的内容包括:名言 ID 与正文、创建时间、缓存节点区域(Cache Node Region)、缓存最近修改时间(Cached Modified at)、缓存状态(Cache status)以及请求耗时(Time taken)。
其中缓存状态做了直观的可视化:Quote.svelte 中当cacheStatus为ttl或swr时,会以绿色高亮并追加 "CACHE HIT" 字样;否则以红色显示。这使得缓存是否命中一眼可辨。
VITE_PUBLIC_URL 的双端适配
前端请求 API 时使用的地址来自 src/lib/utils/helper.ts:
export const HOST = browser ? import.meta.env.VITE_PUBLIC_URL : process.env.VITE_PUBLIC_URL;它根据运行环境区分取值方式:浏览器端读取 Vite 暴露的import.meta.env.VITE_PUBLIC_URL,服务端则读取process.env.VITE_PUBLIC_URL,保证 SSR 与浏览器两种场景下都能拿到正确的 API 主机地址。
如何验证缓存是否生效
启动应用后,可以通过以下两个信号验证缓存策略的实际效果:
- 观察
Cache status与命中标识:TTL/SWR 策略的卡片在缓存生效期会显示绿色 "CACHE HIT";刷新页面后,ttl、swr状态会持续出现,而miss表示该次请求穿透到了数据库。四张卡片中 "No caching" 的卡片理论上始终不会命中缓存(状态为none),可作为对照组。 - 对比
Time taken耗时:每张卡片都会展示本次请求耗时。多次点击 "Refresh Page" 刷新页面,缓存命中时响应耗时通常会显著低于未命中时,从而直观感受到缓存对数据库负载和延迟的削减。
另外,卡片上的region(缓存节点区域)与lastModified(缓存修改时间)字段来自 Accelerate 返回的元数据,可以进一步确认请求确实经过了 Accelerate 缓存节点。
结语:沿着示例继续深入
accelerate/svelte-starter的价值在于把"Prisma ORM + SvelteKit + Accelerate 缓存"这条链路压缩成了一个最小可运行闭环:一张表、一个查询封装、一个 API 路由,就能完整覆盖 TTL、SWR、TTL+SWR 与无缓存四种模式。如果你想进一步探索:
- 阅读 API 路由源码 理解策略分发与校验的完整逻辑;
- 阅读 查询封装 与 类型定义 掌握
cacheStrategy与AccelerateInfo的用法; - 查看 种子脚本 与 Schema 定义 了解数据初始化方式;
- 仓库的 accelerate 目录下还提供了 Next.js、Nuxt、Astro、Remix 等框架的同主题 Starter,可横向对照不同框架下 Accelerate 的接入差异。
- 示例工程
【免费下载链接】prisma-examples
🚀 Ready-to-run Prisma example projects
相关推荐
react-color与SWR集成:数据获取与缓存策略
react color与SWR集成:数据获取与缓存策略 你是否在React项目中遇到过颜色选择器与远程数据同步的难题?当用户从调色板选择颜色后,如何高效保存到后
前端UI组件Magic Resume缓存策略:SWR与React Query的数据缓存
Magic Resume缓存策略:SWR与React Query的数据缓存 痛点:简历数据同步的挑战 在简历编辑应用中,数据同步和缓存管理是核心挑战。用户期望:
前端后端AI 应用高性能缓存策略选型:go-zero中TTL与LRU缓存的实战对比
高性能缓存策略选型:go zero中TTL与LRU缓存的实战对比 在高并发服务开发中,缓存是提升性能的关键技术,但选择不当可能导致缓存雪崩、数据一致性等问题。本
后端RPC框架Web框架微服务API网关服务注册发现代码生成
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考