news 2026/9/15 9:59:19

Wasp 数据库完全指南:SQLite/PostgreSQL 配置、种子数据与 Prisma Client 定制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Wasp 数据库完全指南:SQLite/PostgreSQL 配置、种子数据与 Prisma Client 定制

Wasp 数据库完全指南:SQLite/PostgreSQL 配置、种子数据与 Prisma Client 定制

【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp

导读

本文以 Wasp(version-0.17 文档体系)中的数据模型为基础,系统讲解 Wasp 框架如何处理数据库:从默认的 SQLite、生产级 PostgreSQL 两种后端的选择与连接方式,到 SQLite 迁移 PostgreSQL 的完整步骤,再到用wasp db seed填充种子数据、通过app.db.prismaSetupFn定制 Prisma Client。读完本文,你将掌握在 Wasp 项目中配置数据库、管理连接字符串、编写并运行种子函数,以及为 Prisma Client 注入日志与扩展的全部实战技能。

数据模型、数据库与 Wasp 的关系

在 Wasp 中,Entities、Operations 和 Automatic CRUD 共同构成了操作应用数据的高层接口:Entities 定义数据形状,Operations 封装读写逻辑,CRUD 则把常见增删改查自动暴露出来。但无论接口多么友好,数据终究要落到某个存储引擎里——这就是数据库层要解决的问题。

从源码结构看,Wasp 的数据库抽象集中在waspc/src/Wasp/AppSpec/App/Db.hs,其中Db记录类型只包含两个可选字段:

data Db = Db { seeds :: Maybe [ExtImport], prismaSetupFn :: Maybe ExtImport }

也就是说,在 Wasp 的应用规格(AppSpec)层面,数据库相关的声明只有两块:种子函数列表seeds)与Prisma Client 设置函数prismaSetupFn)。而真正的底层交互统一委托给 Prisma——这正是全文反复出现schema.prisma与 Prisma Client 的原因。本文后续的「Seeding the Database」与「Customising the Prisma Client」两节,即对应这两个字段的实战用法。

支持的数据库后端

Wasp 支持多种数据库后端,实际由DbSystem枚举(PostgreSQL | SQLite,见waspc/src/Wasp/AppSpec/App/Db.hs)约束,下面逐一说明。

SQLite:零配置的默认后端

SQLite 是 Wasp 的默认数据库。新建 Wasp 项目时,schema.prisma中默认的datasource就是 SQLite:

datasource db { provider = "sqlite" url = env("DATABASE_URL") } // ...

关于 Wasp 如何使用 Prisma schema 文件,可进一步阅读 Prisma schema 文件 一节。使用 SQLite 时,DATABASE_URL环境变量由 Wasp 自动设置,你完全无需关心连接细节。

SQLite 非常适合新项目起步:不需要任何安装与配置,开箱即用。但要注意它的适用边界——SQLite 只能用于开发阶段。一旦应用要部署到生产环境,就必须切换到 PostgreSQL,并从此固定使用 PostgreSQL。好在从 SQLite 迁移到 PostgreSQL 的过程相当简单,见下文「从 SQLite 迁移到 PostgreSQL」一节。

PostgreSQL:生产环境的推荐后端

PostgreSQL 是最先进的开源数据库之一,也是全球最流行的数据库之一,经历了 20 多年的持续迭代,是久经考验的成熟选择。若要在 Wasp 中使用 PostgreSQL,只需把schema.prisma中的provider改为"postgresql"

datasource db { provider = "postgresql" url = env("DATABASE_URL") } // ...

作为对照,仓库中的示例项目 kitchen-sink 正是采用 PostgreSQL 配置,其 schema.prisma 开头即为:

datasource db { provider = "postgresql" // Wasp requires that the url is set to the DATABASE_URL environment variable. url = env("DATABASE_URL") }

注意文件中的注释:Wasp 要求url必须指向DATABASE_URL环境变量。同时该文件还包含 Wasp 必需的prisma-client-jsgenerator:

generator client { provider = "prisma-client-js" }

与 SQLite 不同,使用 PostgreSQL 时你需要保证一个运行中的数据库实例,因为wasp startwasp db migrate-dev等命令都依赖数据库可达。所有受支持的连接方式见下文「连接数据库」一节。

连接数据库

SQLite:无需任何操作

使用 SQLite 时,你不需要为连接做任何特殊处理,Wasp 会自动管理一切。

PostgreSQL:两种连接方式

使用 PostgreSQL 时,Wasp 提供两种连接方式,按需选择:

  1. 托管体验:让 Wasp 为你启动一个开箱即用的开发数据库;
  2. 完全掌控:通过数据库 URL 连接到你自行准备的外部数据库。
方式一:使用 Wasp 提供的开发数据库

运行wasp start db即可启动一个默认的 PostgreSQL 开发数据库,你的应用会自动连接上去——只要让wasp start db保持在后台运行即可。使用前请确认:

  • 已安装 Docker,且docker命令位于PATH中;
  • 端口5432未被占用。

提示:如果你希望用psql、pgAdmin 等外部工具连接该开发数据库,连接凭据会在执行wasp db start时打印在控制台最开头,留意即可。

方式二:连接已有数据库

如果你想自建开发数据库或连接外部数据库,可以通过DATABASE_URL环境变量告诉 Wasp 连接字符串。最直接的方式是把变量写入项目根目录的 .env.server 文件(文件不存在则新建):

DATABASE_URL=postgresql://user:password@localhost:5432/mydb

也可以在执行wasp命令时以内联方式设置(这一技巧对所有环境变量通用):

DATABASE_URL=<my-db-url> wasp ...

内联方式非常适合对某个特定数据库执行单次命令,例如针对刚创建的 staging 或生产数据库做种子填充:

DATABASE_URL=<production-db-url> wasp db seed myProductionSeed

关于种子数据的更多说明见「种子数据(Seeding the Database)」一节。

从 SQLite 迁移到 PostgreSQL

要把 Wasp 应用部署到生产环境,必须先切换到 PostgreSQL。完整步骤如下:

  1. 修改 provider:在schema.prisma中把provider改为"postgresql"

    datasource db { // highlight-next-line provider = "postgresql" url = env("DATABASE_URL") } // ...
  2. 清理旧迁移与旧库:删除migrations/目录下的所有旧迁移(它们是 SQLite 迁移,无法用于 PostgreSQL),同时清理 SQLite 数据库文件,执行wasp clean

    rm -r migrations/ wasp clean
  3. 启动新数据库:确保新的 PostgreSQL 数据库已运行(方法见「连接数据库」一节),并保持其运行状态,因为下一步需要它。

  4. 生成新初始迁移:在另一个终端中运行wasp db migrate-dev,应用改动并创建新的初始迁移。

  5. 完成:迁移到此结束。

种子数据(Seeding the Database)

数据库种子(seeding)指用一批初始数据填充数据库的过程,最常见的用途有两种:

  1. 把开发数据库调整到便于开发与测试的状态;
  2. 为任意数据库(devstagingprod)初始化其运行所必需的基础数据,例如用默认货币填充 Currency 表、用所有可用国家填充 Country 表。

编写种子函数

你可以在app.db.seeds数组下定义任意数量的种子函数

app MyApp { // ... db: { seeds: [ import { devSeedSimple } from "@src/dbSeeds.js", import { prodSeed } from "@src/dbSeeds.js" ] } }

(JavaScript 与 TypeScript 项目的写法一致,TypeScript 示例同样适用。)

每个种子函数必须是异步函数,接收一个参数prisma——即用于与数据库交互的 Prisma Client 正是这样声明的:

import { type Db } from "@wasp.sh/spec"; import { setUpPrisma } from "./prisma" with { type: "ref" }; import { devSeedSimple, prodSeed } from "./seeds" with { type: "ref" }; export const db: Db = { seeds: [devSeedSimple, prodSeed], prismaSetupFn: setUpPrisma, };

种子函数属于服务端代码,因此可以导入其他服务端函数——这很方便,因为你可能想借助 Action 来执行种子写入。下面是一个导入 Action 的种子函数示例(JavaScript 版):

import { createTask } from './actions.js' import { sanitizeAndSerializeProviderData } from 'wasp/server/auth' export const devSeedSimple = async (prisma) => { const user = await createUser(prisma, { username: 'RiuTheDog', password: 'bark1234', }) await createTask( { description: 'Chase the cat' }, { user, entities: { Task: prisma.task } } ) } async function createUser(prisma, data) { const newUser = await prisma.user.create({ data: { auth: { create: { identities: { create: { providerName: 'username', providerUserId: data.username, providerData: await sanitizeAndSerializeProviderData({ hashedPassword: data.password }), }, }, }, }, }, }) return newUser }

TypeScript 版本如下:

import { createTask } from './actions.js' import type { DbSeedFn } from 'wasp/server' import { sanitizeAndSerializeProviderData } from 'wasp/server/auth' import type { AuthUser } from 'wasp/auth' import type { PrismaClient } from 'wasp/server' export const devSeedSimple: DbSeedFn = async (prisma) => { const user = await createUser(prisma, { username: 'RiuTheDog', password: 'bark1234', }) await createTask( { description: 'Chase the cat', isDone: false }, { user, entities: { Task: prisma.task } } ) }; async function createUser( prisma: PrismaClient, data: { username: string, password: string } ): Promise<AuthUser> { const newUser = await prisma.user.create({ data: { auth: { create: { identities: { create: { providerName: 'username', providerUserId: data.username, providerData: await sanitizeAndSerializeProviderData<'username'>({ hashedPassword: data.password }), }, }, }, }, }, }) return newUser }

Wasp 导出了一个DbSeedFn类型,可方便地为种子函数标注类型:

type DbSeedFn = (prisma: PrismaClient) => Promise<void>

devSeedSimple标注该类型后,TypeScript 会获得两点保障:参数prisma的类型为PrismaClient;返回值类型为Promise<void>

仓库中 kitchen-sink 的真实实现 seeds.ts 与此一致:devSeedSimple创建用户martinsos(密码test1234)并为其创建初始任务,prodSeed则以martinsosProd用户创建面向生产的种子数据,并在末尾打印提示日志,例如:

export const devSeedSimple: DbSeedFn = async (prismaClient) => { const user = await createUser(prismaClient, { username: "martinsos", password: "test1234", }); await createTask( { description: "My initial task" }, { user, entities: { Task: prismaClient.task } }, ); console.log("Did simple dev seed!"); };

运行种子函数

运行wasp db seed后,Wasp 会询问你要执行哪个种子函数(前提是你定义了不止一个);也可以用wasp db seed <seed-name>直接指定:

wasp db seed devSeedSimple

关于这两个命令的完整说明见下文「API 参考」。

提示:通常你会希望在wasp db reset之后立即执行wasp db seed——清空数据库后正好需要重新填充初始数据。

种子脚本的底层机制

从源码看,wasp db seed最终生成并运行一个种子脚本。Wasp 的服务端模板 dbSeed.ts 展示了其运行原理:所有在app.db.seeds中声明的种子函数被收集到一个seeds对象中,脚本通过环境变量WASP_DB_SEED_NAME(定义于waspc/src/Wasp/Generator/ServerGenerator/Db/Seed.hs)读取要执行的种子函数名,然后以wasp/server导出的共享prisma实例调用之,结束后统一$disconnect

const seeds = { devSeedSimple, prodSeed, } async function main() { const nameOfSeedToRun = process.env.WASP_DB_SEED_NAME if (nameOfSeedToRun) { console.log(`Running seed: ${nameOfSeedToRun}`) } else { console.error('Name of the seed to run not specified!') } await (seeds[nameOfSeedToRun] satisfies DbSeedFn)(prisma) }

wasp db seed <seed-name>命令正是把<seed-name>以该环境变量的形式注入后执行脚本(见waspc/src/Wasp/Generator/DbGenerator/Jobs.hs(dbSeedNameEnvVarName, seedName)的传递)。这解释了为什么种子函数名必须与import表达式中的标识符一致。

定制 Prisma Client

Wasp 通过 Prisma Client 与数据库交互。如需定制客户端,在app.db.prismaSetupFn字段中定义一个返回 Prisma Client 实例的函数即可。这允许你配置 日志 或 客户端扩展 等功能。

main.wasp中声明:

app MyApp { title: "My app", // ... db: { prismaSetupFn: import { setUpPrisma } from "@src/prisma" } }

对应的src/prisma.js实现(JavaScript):

import { PrismaClient } from '@prisma/client' export const setUpPrisma = () => { const prisma = new PrismaClient({ log: ['query'], }).$extends({ query: { task: { async findMany({ args, query }) { args.where = { ...args.where, description: { not: { contains: 'hidden by setUpPrisma' } }, } return query(args) }, }, }, }) return prisma }

TypeScript 实现(src/prisma.ts)与上述逻辑一致:先开启log: ['query']查询日志,再通过$extendstask模型的findMany注入过滤条件——任何描述中包含hidden by setUpPrisma的任务都不会被查出,这展示了如何在查询层统一做横切改造。

仓库中 kitchen-sink 的 prisma.ts 更进一步,同时演示了result扩展:为所有模型注入一个计算字段_extraField,用来验证 Prisma 类型能够贯穿整个 RPC 调用栈:

export const setUpPrisma = () => { const prisma = new PrismaClient({ // Log SQL queries if needed // log: ['query'], }).$extends({ query: { task: { async findMany({ args, query }) { args.where = { ...args.where, description: { not: { contains: "hidden by setUpPrisma" } }, }; return query(args); }, }, }, result: { $allModels: { _extraField: { needs: {}, compute() { return "Some string!" as const; }, }, }, }, }); return prisma; };

可以看到,prismaSetupFn的职责边界很清晰:接收配置、返回一个配置好的 Prisma Client 实例,后续所有数据库访问都经由该实例完成。因此它也是接入日志、软删除、多租户过滤、审计字段等通用能力的统一入口。

API 参考

app.db是一个字典,包含以下字段(所有字段均为可选):

app MyApp { title: "My app", // ... db: { seeds: [ import devSeed from "@src/dbSeeds" ], prismaSetupFn: import { setUpPrisma } from "@src/prisma" } }

(JavaScript 与 TypeScript 写法相同。)

  • seeds: [ExtImport]

    定义种子函数,供wasp db seed命令用初始数据填充数据库。详见「种子数据(Seeding the Database)」一节。

  • prismaSetupFn: ExtImport

    定义设置 Prisma Client 的函数,Wasp 期望它返回一个 Prisma Client 实例。可用于配置 日志 或 客户端扩展:

    import { PrismaClient } from '@prisma/client' export const setUpPrisma = () => { const prisma = new PrismaClient({ log: ['query', 'info', 'warn', 'error'], }) return prisma }

    waspc/src/Wasp/AppSpec/App/Db.hsDb记录的两个可选字段一一对应,两字段在 AppSpec 层均为ExtImport(外部导入引用)。

种子数据库的 CLI 命令

  • wasp db seed

    如果只定义了一个种子函数,直接运行它;如果定义了多个,则以交互方式让你选择。

  • wasp db seed <seed-name>

    直接运行指定名称的种子函数。该名称即app.db.seeds列表中import表达式所用的标识符。例如,对于如下定义的devSeedSimple

    app MyApp { // ... db: { seeds: [ // ... import { devSeedSimple } from "@src/dbSeeds.js", ] } }

    运行命令:

    wasp db seed devSeedSimple

小结

  • 后端选择:开发期用零配置的 SQLite(默认),生产环境切换到 PostgreSQL 并固定下来;两者都只需修改schema.prismaprovider
  • 连接方式:SQLite 全自动;PostgreSQL 可用wasp start db启动托管开发库,或用DATABASE_URL(写入.env.server或内联)连接自建/外部数据库。
  • 迁移路径:改 provider → 删除旧迁移并wasp clean→ 启动新库 →wasp db migrate-dev生成新初始迁移。
  • 种子数据:在app.db.seeds声明任意数量的异步种子函数,用wasp db seed [name]执行;种子脚本通过WASP_DB_SEED_NAME环境变量定位目标函数。
  • 定制客户端:在app.db.prismaSetupFn返回自定义 Prisma Client,可开启日志、注入查询扩展与结果扩展。

配合 Entities、Operations 与 Automatic CRUD,这套数据库能力构成了 Wasp 完整的数据链路:既提供了 SQLite 到 PostgreSQL 的平滑升级路径,也保留了通过种子与 Prisma 扩展深度定制数据库行为的自由度。

【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 9:57:18

如何免费批量下载抖音无水印视频:douyin-downloader 完整指南

如何免费批量下载抖音无水印视频&#xff1a;douyin-downloader 完整指南 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallbac…

作者头像 李华
网站建设 2026/9/15 9:56:06

多串口工控主板与串口服务器的本质区别及选型指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/15 9:54:12

ajs17网站建设:保姆级建站教程教你解决没人访问难题

ajs17网站建设:保姆级建站教程教你解决没人访问难题 网站上线三个月,后台每天只有三个访问,全是机器人。这种“死寂”是绝大多数中小企业老板最头疼的事。你花了大几万做站点,结果成了摆设,这就是典型的 网站做好了没人访问 。别再盲目找外包团队加功能了,问题不在代码,而在运营逻辑。 今天这篇…

作者头像 李华
网站建设 2026/9/15 9:53:29

服务器多IP配置实战:从端口限制到高并发编排

做 YouTube 数据采集和视频自动化处理的人&#xff0c;迟早会遇到同一个困惑&#xff1a;代码里多线程、异步、协程都上了&#xff0c;请求一多还是 timeout、断连&#xff0c;甚至被对方限住。我整理“YouTube 海量视频并发”这个系列的时候&#xff0c;发现很多问题不是出在语…

作者头像 李华
网站建设 2026/9/15 9:52:07

COMSOL复现液氮致岩石热损伤:三场耦合建模与调试全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华