新闻详情

新闻详情

首页 / 资讯中心 / 详情

Wasp Queries 实战指南:用声明式方式实现只读数据查询与全栈类型安全

发布时间:2026/9/13 20:24:53来源:尧图网络
Wasp Queries 实战指南:用声明式方式实现只读数据查询与全栈类型安全
Wasp Queries 实战指南用声明式方式实现只读数据查询与全栈类型安全【免费下载链接】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/waspWasp 的 Query查询机制让你无需手写 HTTP API、服务端路由与客户端缓存逻辑即可从服务端安全地读取数据并在全栈范围内调用。本文基于仓库内 web/docs/data-model/operations/queries.md 官方文档展开结合 examples/kitchen-sink 的真实示例与 wasp.sh/spec 源码完整讲解 Query 的声明、实现、调用、错误处理与类型安全读完即可在 Wasp 应用中落地可复用的只读数据层。Query 是什么Wasp 中的只读数据操作在 Wasp 的数据模型体系中Entities 负责定义应用的数据结构与关系而Operations操作负责与这些数据打交道。Operations 分为两类Queries查询只读数据不修改服务端状态Actions操作修改或新增数据。在 operations 总览文档 中二者的分工被概括为一句话Queries 用于读取数据Actions 用于改变数据更新既有记录或创建新记录。适合使用 Query 的场景非常典型拉取一篇博客文章下的所有评论、获取点赞某个视频的用户列表、根据 ID 查询某个产品的详情——这些只读操作都是 Query 的完美用例。:::tip 与 Action 的对比 Query 与 Action 在 API 上高度相似Action 的指南同样适用于理解 Query。两者的核心差异在于Query 只允许读取服务端状态Action 可以且通常应该修改服务端状态。Wasp 依赖这一约定来进行前端缓存失效因此遵守读用 Query、写用 Action的规范至关重要。详细差异见下文Query 与 Action 的异同一节。 :::创建 Query 的两步流程创建一个 Query 只需两步在 Wasp 文件中使用queryspec 声明 Query实现 Query 的 NodeJS 函数。完成这两步后Wasp 会自动生成对应的客户端与服务端调用代码你可以在代码库的任意位置客户端或服务端使用该 Query。你无需自己构建 HTTP API、管理服务端请求处理甚至无需关心客户端的响应处理和缓存——只需要专注于 Query 内部的业务逻辑剩下的交给 Wasp。声明 QuerySpecifying Queries在main.wasp.ts中通过query构造函数声明 Query。以下示例声明了两个 Query一个用于获取全部任务另一个根据筛选条件如任务是否完成获取任务import { app, query } from wasp.sh/spec import { getAllTasks, getFilteredTasks } from ./src/queries with { type: ref } export default app({ // ... spec: [ query(getAllTasks), query(getFilteredTasks), ], })注意导入语句末尾的with { type: ref }根据 参考导入说明这告诉 Wasp 把导入当作对应用代码的引用而不会真正执行被导入的代码。更完整的参考导入语法说明见 web/docs/general/spec.md 中的 reference imports 一节。此处你引用的是尚不存在的实现函数——这没关系。Wasp 的理念是先有高层概念Wasp 文件中的 Query spec再处理实现细节JavaScript 中的 Query 实现。声明之后Wasp 会从传给query的函数名推导出 Query 的名称query(getFilteredTasks)会创建一个名为getFilteredTasks的 Query。随后发生两件重要的事Wasp生成一个以 Query 命名的服务端 NodeJS 函数Wasp生成一个以 Query 命名的客户端 JavaScript 函数如getFilteredTasks。该函数接收一个可选的参数——一个包含任意可序列化数据的对象Wasp 会通过网络发送该对象并将其作为第一个位置参数传入 Query 的实现。这种抽象之所以成立是因为 Wasp 在服务端生成了一个 HTTP API 路由处理器在其内部调用 Query 的 NodeJS 实现。生成的这两个同名函数保证了整个应用客户端与服务端拥有一致的调用接口。实现 QueryImplementing Queries in Node声明之后需要在src/queries.{js,ts}中导出实现Wasp 会从这里查找。下面是getAllTasks与getFilteredTasks的完整实现JavaScript 版本src/queries.js// our database const tasks [ { id: 1, description: Buy some eggs, isDone: true }, { id: 2, description: Make an omelette, isDone: false }, { id: 3, description: Eat breakfast, isDone: false }, ] // You dont need to use the arguments if you dont need them export const getAllTasks () { return tasks } // The args object is something sent by the caller (most often from the client) export const getFilteredTasks (args) { const { isDone } args return tasks.filter((task) task.isDone isDone) }TypeScript 版本src/queries.tsimport { type GetAllTasks, type GetFilteredTasks } from wasp/server/operations type Task { id: number description: string isDone: boolean } // our database const tasks: Task[] [ { id: 1, description: Buy some eggs, isDone: true }, { id: 2, description: Make an omelette, isDone: false }, { id: 3, description: Eat breakfast, isDone: false }, ] // You dont need to use the arguments if you dont need them export const getAllTasks: GetAllTasksvoid, Task[] () { return tasks } // The args object is something sent by the caller (most often from the client) export const getFilteredTasks: GetFilteredTasks PickTask, isDone, Task[] (args) { const { isDone } args return tasks.filter((task) task.isDone isDone) }Payload 序列化约束superjson根据 操作文档附注Wasp 底层使用 superjson 进行序列化。这意味着你不只限于发送和接收 JSON 载荷——bigint、Date、Map、Set以及Prisma.Decimal等 superjson 支持的额外类型都会由 Wasp 自动处理序列化与反序列化。在 TypeScript 中只要用正确的自动生成类型标注 Operation编译器就能保证 payload 是合法的即 Wasp 知道如何序列化/反序列化它们。Query 的类型支持Wasp 会根据 Wasp 文件中的 spec 自动生成GetAllTasks、GetFilteredTasks这类泛型类型用来标注 Query 实现。这是可选的但非常有用因为正确标注后TypeScript 会知道context.entities对象必须包含Task实体TypeScript 会知道context对象是否包含用户信息取决于 Query 是否使用 auth。生成的类型接受两个可选类型参数Input——Query 函数接收的参数payload类型Output——Query 函数的返回类型。用上面的代码举例getAllTasks不接收任何参数输入类型为void但返回任务列表输出类型为Task[]getFilteredTasks期望接收{ isDone: boolean }类型的对象由Task实体类型派生。如果省略两个类型参数TypeScript 会推断最宽泛的类型输入为never、输出为unknown。如果你不希望 Query 接收或返回任何值请使用void作为类型参数。指定Input/Output完全是可选的但强烈推荐它能带来在实现内部获得参数与返回值的类型支持全栈类型安全full-stack type safety——客户端调用处的类型永远与服务端实现匹配。用satisfies推断返回类型如果不想显式写出 Query 的返回类型可以用 TypeScript 的satisfies关键字让编译器自动推断const getFoo (async (_args, context) { const foos await context.entities.Foo.findMany() return { foos, message: Here are some foos!, queriedAt: new Date(), } }) satisfies GetFoo从这个片段中TypeScript 可以推断出context的正确类型以及 Query 的返回类型是{ foos: Foo[], message: string, queriedAt: Date }。如果不需要context甚至可以完全跳过类型标注与参数const getFoo () ({ name: Foo, date: new Date() })使用 Query在客户端调用 Query在客户端从wasp/client/operations导入 Query 并直接调用即可import { getAllTasks, getFilteredTasks } from wasp/client/operations // ... const allTasks await getAllTasks() const doneTasks await getFilteredTasks({ isDone: true })调用方式不因 Query 是否要求登录而改变——Wasp 会在后台自动认证已登录用户。在 TypeScript 中客户端代码会自动获得类型安全import { getAllTasks, getFilteredTasks } from wasp/client/operations // TypeScript automatically infers the return values and type-checks // the payloads. const allTasks await getAllTasks() const doneTasks await getFilteredTasks({ isDone: true })你只需要在服务端实现中指定 Query 的类型客户端代码就会自动知道其 API payload 类型——这就是 Wasp 的自动全栈类型安全。在服务端调用 Query在服务端调用 Query 与客户端类似但有两点不同从wasp/server/operations而不是wasp/client/operations导入对于需要认证的 Query必须传入带有user字段的context对象——context的其他部分如 Entities无需手动传入会自动注入。import { getAllTasks, getFilteredTasks } from wasp/server/operations const user // Get an AuthUser object, e.g., from context.user in an operation. // ... const allTasks await getAllTasks({ user }) const doneTasks await getFilteredTasks({ isDone: true }, { user })TypeScript 版本同样会自动推断返回值并做 payload 类型检查。用useQuery钩子实现响应式查询在客户端使用 Query 时可以用useQuery钩子让数据响应式。这个钩子随 Wasp 内置是react-query的useQuery钩子的薄封装唯一区别是你无需提供缓存 key——Wasp 会在底层自动处理。下面是完整的组件示例src/MainPage.jsx/src/MainPage.tsximport React from react import { useQuery, getAllTasks, getFilteredTasks } from wasp/client/operations const MainPage () { const { data: allTasks, error: error1 } useQuery(getAllTasks) const { data: doneTasks, error: error2 } useQuery(getFilteredTasks, { isDone: true, }) if (error1 ! null || error2 ! null) { return divThere was an error/div } return ( div h2All Tasks/h2 {allTasks allTasks.length 0 ? allTasks.map((task) Task key{task.id} {...task} /) : No tasks} h2Finished Tasks/h2 {doneTasks doneTasks.length 0 ? doneTasks.map((task) Task key{task.id} {...task} /) : No finished tasks} /div ) } const Task ({ description, isDone }: Task) { return ( div p strongDescription: /strong {description} /p p strongIs done: /strong {isDone ? Yes : No} /p /div ) } export default MainPageTypeScript 版本中你同样不需要手动标注 Query 的返回值类型——Wasp 会自动从后端实现推断。这正是全栈类型安全的含义客户端上的类型永远与服务端一致。注意useQuery的第一个参数直接传入 Query 函数本身如getAllTasks第二个参数是传给 Query 的 payload 对象如{ isDone: true }。错误处理出于安全考虑Query 的 NodeJS 实现中抛出的所有异常都会以 HTTP 状态码500发送给客户端并且移除所有其他细节。默认隐藏错误细节有助于避免通过网络意外泄露敏感信息。如果确实想向客户端传递额外的错误信息可以在实现中构造并抛出HttpErrorimport { type GetAllTasks } from wasp/server/operations import { HttpError } from wasp/server export const getAllTasks: GetAllTasks async (args, context) { throw new HttpError( 403, // status code You cant do this!, // message { foo: bar } // data ) }当状态码为4xx时客户端会收到包含对应message和data字段的响应对象并重新抛出包含这些字段的错误。为防止信息泄露对于其他任何 HTTP 状态码服务端都不会转发这些字段。这一点在仓库示例中得到了实践在 examples/kitchen-sink/src/features/operations/queries.ts 中getTask在任务不存在时抛出HttpError(404)而当任务属于其他用户时也抛出HttpError(404)——代码注释明确说明这是为了隐藏被禁止访问的目标资源是否存在防范 IDOR 类漏洞的安全措施。在 Query 中使用 Entities大多数情况下Query 操作的资源是 Entities。要在 Query 中使用 Entity需要在queryspec 中把它加入entities选项import { app, query } from wasp.sh/spec import { getAllTasks, getFilteredTasks } from ./src/queries with { type: ref } export default app({ // ... spec: [ query(getAllTasks, { entities: [Task] }), query(getFilteredTasks, { entities: [Task] }), ], })Wasp 会把指定的 Entity 注入 Query 的context参数让你在实现中直接使用其 Prisma APIimport { type Task } from wasp/entities import { type GetAllTasks, type GetFilteredTasks } from wasp/server/operations export const getAllTasks: GetAllTasksvoid, Task[] async (args, context) { return context.entities.Task.findMany({}) } export const getFilteredTasks: GetFilteredTasks PickTask, isDone, Task[] async (args, context) { return context.entities.Task.findMany({ where: { isDone: args.isDone }, }) }context.entities.Task对象暴露的就是 Prisma 的 CRUD API如findMany、findUnique、count等。再次强调标注 Query 是可选的但能显著提升全栈类型安全。关于auth选项queryspec 还支持auth配置。在 examples/kitchen-sink/src/features/operations/operations.wasp.ts 中可以看到query(getNumTasks, { entities: [Task], auth: false })的用法——它明确关闭了该 Query 的认证要求。这一选项在 spec 映射源码 中被解构出来用于控制 Query 是否自动注入用户认证上下文。Query 与 Action 的异同Query 与 Action 是 Wasp 中两个紧密相关的概念理解它们的区别至关重要Action 可以且通常应该修改服务端状态而 Query 只允许读取状态。Wasp 进行缓存失效时依赖你遵守这一约定因此务必遵守。Action 不需要响应式可以直接调用但 Wasp 也提供了useActionReact 钩子为 Action 增加额外行为如乐观更新。actionspec 与queryspec 基本一致唯一的区别在于 spec 的名字。相关的缓存失效机制由于 Wasp 使用react-query管理 Query 缓存需要确保数据过期时失效缓存。手动失效refetch、直接 invalidation容易出错因此 Wasp 提供了开箱即用的基于 Entity 的自动缓存失效因为 Action 会修改状态而 Query 读取状态所以每当一个使用某 Entity 的 Action 被执行时Wasp 就会失效使用同一 Entity 的 Query 缓存。例如ActioncreateTask与 QuerygetTasks都使用 EntityTask执行createTask可能使getTasks的缓存结果过期Wasp 会将其失效并触发重新拉取。这意味着 Wasp 让 Query 保持新鲜而无需你操心缓存失效。该机制的详细说明见 web/docs/data-model/operations/actions.md 的 Cache Invalidation 一节。如果这种自动失效不够精细可能产生不必要的更新你可以使用react-query提供的机制Wasp 的useAction钩子目前是唯一原生支持的手动缓存机制乐观更新。在 TypeScript 中还可以通过任意 Query 的queryCacheKey属性直接获取其内部缓存 key以便使用react-query底层 API。API 参考声明 QuerySpecifying Queries声明一个 Query 后你可以在代码任意位置服务端或客户端导入并使用它。例如声明为getFoo的 QueryWasp 会生成两个同名函数// Use it on the client import { getFoo } from wasp/client/operations // Use it on the server import { getFoo } from wasp/server/operations在 TypeScript 中它还会生成一个可在服务端导入的类型import { type GetFoo } from wasp/server/operationsqueryspec 的所有支持选项可以在 spec 包源码 中查看构造函数query(fn, config?)接收实现引用fn和可选配置config返回{ kind: query, fn, ...config }QueryConfig由entities、auth等可选字段组成。实现 QueryImplementing QueriesQuery 的实现是一个接收两个参数的 NodeJS 函数需要await时可以是async函数。由于两个参数都是位置参数参数名可以随意命名但约定俗成用args和contextargs类型取决于 Query——调用 Query 时传入的数据对象如筛选条件。参阅上面的使用 Query一节了解如何传入。context类型取决于 Query——由 Wasp 传入的附加上下文对象包含用户会话信息与实体信息。context.entities的用法见在 Query 中使用 Entitiescontext.user的用法见 web/docs/auth/overview.md。在 TypeScript 中声明 Query 后 Wasp 会生成泛型类型声明为getSomething的 Query 对应类型GetSomething接收两个可选类型参数Input——args对象的类型Query 的输入 payload默认值为neverOutput——Query 返回值的类型Query 的输出 payload默认值为unknown。默认值被设计为尽可能宽松如果希望 Query 不接收/不返回任何内容请使用void作为类型参数。完整示例import { app, query } from wasp.sh/spec import { getFoo } from ./src/queries with { type: ref } export default app({ // ... spec: [ query(getFoo, { entities: [Foo] }), ], })上面的声明期望在src/queries.ts中找到命名导出getFoo。使用生成的类型GetFoo并指定输入输出import { type GetFoo } from wasp/server/operations type Foo // ... export const getFoo: GetFoo{ id: number }, Foo (args, context) { // implementation };这里 Query 期望接收一个含id字段类型number的对象作为args并返回类型为Foo的值。useQuery钩子Wasp 的useQuery钩子是react-queryuseQuery钩子的薄封装关键区别是无需提供缓存 key——Wasp 在底层代劳。它接收三个参数queryFn必填——Wasp 根据 Wasp 文件中的queryspec 生成的客户端 Query 函数。queryFnArgs——希望传入 Query 的参数对象payloadQuery 的 NodeJS 实现会将其作为第一个位置参数接收。options——一个react-query的options对象用于改变某个 Query 的默认行为。若要修改全局默认值可以在 客户端配置函数 中进行设置。仓库实战佐证kitchen-sink 中的完整 Query 链路examples/kitchen-sink 是仓库内覆盖最全面的示例应用其 operations 模块展示了 Query 的完整落地形态声明operations.wasp.tsgetTasks、getNumTasks、getTask、getOldestTask四个 Query 均声明了{ entities: [Task] }其中getNumTasks额外设置了auth: false。实现queries.tsgetTasks使用satisfies GetTasksvoid让 TS 推断返回值getTask用GetTaskPickTask, id, Task标注输入输出并在实现中结合context.user做权限校验IDOR 防护getNumTasks演示了context.entities.Task.count()的用法。客户端响应式使用components/Todo.tsxconst { data: tasks, isError, error: tasksError } useQuery(getTasks)直接消费 Query 结果并处理错误状态。这条从 Wasp 文件声明、NodeJS 实现到 React 组件消费的完整链路正是本指南所有概念的落地验证。配套的 cacheInvalidation.test.ts 还覆盖了基于 Entity 的自动缓存失效行为可以作为深入理解 Query 缓存语义的阅读材料。【免费下载链接】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),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

深入解析面向服务的架构(SOA):从特性到实施 2026/9/13 23:40:16

深入解析面向服务的架构(SOA):从特性到实施

在当今快速演进的软件工程领域,企业级系统的复杂性与日俱增。如何打破异构系统之间的壁垒,实现业务的快速响应与IT资产的复用,成为了架构师们面临的核心挑战。面向服务的架构(Service-Oriented Architecture, SOA) 应运…

阅读更多 →
AI决策搜索:从信息检索到智能推荐的技术演进 2026/9/13 23:40:16

AI决策搜索:从信息检索到智能推荐的技术演进

1. AI搜索的范式转移:从答案检索到决策支持过去二十年里,搜索引擎的核心逻辑始终围绕关键词匹配展开。用户输入问题,系统返回相关网页链接。但2023年大模型技术的突破性进展,彻底改变了这个游戏规则。当ChatGPT能够直接生成完整答…

阅读更多 →
Beekeeper Studio 数据库连接完全指南:从 TCP/Socket、SSL 到 SSH 隧道与 ~/.ssh/config 2026/9/13 23:40:16

Beekeeper Studio 数据库连接完全指南:从 TCP/Socket、SSL 到 SSH 隧道与 ~/.ssh/config

Beekeeper Studio 数据库连接完全指南:从 TCP/Socket、SSL 到 SSH 隧道与 ~/.ssh/config 【免费下载链接】beekeeper-studio Modern and easy to use SQL client for MySQL, Postgres, SQLite, SQL Server, and more. Linux, MacOS, and Windows. 项目地址: https…

阅读更多 →
Hermes WebUI 高级聊天配置实战:会话召回预填、智能标题与 Gateway 后端桥接 2026/9/13 23:40:16

Hermes WebUI 高级聊天配置实战:会话召回预填、智能标题与 Gateway 后端桥接

Hermes WebUI 高级聊天配置实战:会话召回预填、智能标题与 Gateway 后端桥接 【免费下载链接】hermes-webui Hermes WebUI: The best way to use Hermes Agent from the web or from your phone! 项目地址: https://gitcode.com/GitHub_Trending/he/hermes-webui …

阅读更多 →
pybind11 升级指南:从 v2.0 到 v3.0 的迁移路线、破坏性变更与实战要点 2026/9/13 23:40:16

pybind11 升级指南:从 v2.0 到 v3.0 的迁移路线、破坏性变更与实战要点

pybind11 升级指南:从 v2.0 到 v3.0 的迁移路线、破坏性变更与实战要点 【免费下载链接】pybind11 Seamless operability between C11 and Python 项目地址: https://gitcode.com/GitHub_Trending/py/pybind11 本文是 pybind11 官方 docs/upgrade.rst 升级指…

阅读更多 →
OpenWork Den API 组织路由(Org Routes)架构解析:活动组织模型、成员/邀请/角色/SCIM 全链路实现 2026/9/13 23:37:15

OpenWork Den API 组织路由(Org Routes)架构解析:活动组织模型、成员/邀请/角色/SCIM 全链路实现

OpenWork Den API 组织路由(Org Routes)架构解析:活动组织模型、成员/邀请/角色/SCIM 全链路实现 【免费下载链接】openwork The open-source alternative to Claude Cowork (powered by opencode) 项目地址: https://gitcode.com/GitHub_T…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞