新闻详情

新闻详情

首页 / 资讯中心 / 详情

Wasp 的 Automatic CRUD:用一行声明自动生成 React + Node.js 增删改查后端

发布时间:2026/9/16 15:58:14来源:尧图网络
Wasp 的 Automatic CRUD:用一行声明自动生成 React + Node.js 增删改查后端
Wasp 的 Automatic CRUD用一行声明自动生成 React Node.js 增删改查后端【免费下载链接】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/waspAutomatic CRUD 是 Wasp 框架提供的高层抽象你只需在 Wasp 声明文件中用一段简短的crud声明就能让 Wasp 自动生成针对某个 Prisma 实体的增删改查 Queries 与 Actions并在实体定义变化时自动重新生成后端逻辑。本文以 Wasp 仓库waspc 生成器、代码生成模板为依据从概念、完整实战示例到 API 参考带你掌握声明式后端开发的这套核心能力。为什么需要 Automatic CRUD如果你写过不少全栈应用大概率会发现自己反复在做同一类重复工作列出数据、添加数据、编辑数据、删除数据。这些无聊的部分正是 Automatic CRUD 要消除的对象。在 Wasp 中只需要一条声明就可以告诉框架为某个实体自动生成创建create、读取get / getAll、更新update、删除delete所对应的服务端逻辑——也就是 Wasp 的 Queries 与 Actions。当你更新实体的定义时Wasp 会自动重新生成对应的后端代码实体与接口始终同步。从源码结构看这一能力在 Wasp 编译器中是一条完整的生成流水线waspc/src/Wasp/AppSpec/Crud.hs 定义了crud声明的内部数据结构Crud、CrudOperations、CrudOperationOptions、CrudOperationwaspc/src/Wasp/Generator/Crud.hs 负责把声明转换成生成器所需的 JSON 配置操作名、路由、是否公开、实体名等waspc/src/Wasp/Generator/Crud/Routes.hs 负责生成路由路径waspc/data/Generator/templates/server/src/crud/_operations.ts 等模板文件最终渲染出可运行的 Node.js 实现与客户端 SDK。注意该功能目前处于早期预览阶段Early previewWasp 团队仍在积极迭代中具体规划见文末 CRUD 的未来。核心概念一条 crud 声明做了什么假设我们有如下Task实体schema.prisma 中同样定义了任务类实体便于对照model Task { id Int id default(autoincrement()) description String isDone Boolean }接着在main.wasp中定义名为Tasks的crud。我们指定使用Task实体并启用getAll、get、create、update四个操作假设不需要deletecrud Tasks { entity: Task, operations: { getAll: { isPublic: true, // by default only logged in users can perform operations }, get: {}, create: { overrideFn: import { createTask } from src/tasks, }, update: {}, }, }这段声明做了三件事getAll、get、update使用默认实现create指定了自定义实现overrideFn指向src/tasks.{js,ts}中导出的createTask函数getAll被标记为公开isPublic: true无需登录即可访问其余操作默认私有仅登录用户可调用。Tasks crud 声明的可视化示意图片来源web/static/img/crud_diagram.png声明完成后你就可以在客户端代码中直接使用生成的 Queries 和 Actions 了。接下来用一个完整的 TODO 应用把整个流程跑通。实战示例构建一个带登录的 TODO 应用下面创建一个完整的使用 Automatic CRUD 的应用。我们沿用上一节的Task实体再新增一个User实体并开启基于用户名和密码的认证。基于用户名认证的简易任务应用演示图片来源web/static/img/crud-guide.gif1. 创建应用运行wasp new tasksCrudApp初始化项目然后把以下内容写入main.waspapp tasksCrudApp { wasp: { version: {latestWaspVersion} }, title: Tasks Crud App, // We enabled auth and set the auth method to username and password auth: { userEntity: User, methods: { usernameAndPassword: {}, }, onAuthFailedRedirectTo: /login, }, } // Tasks app routes route RootRoute { path: /, to: MainPage } page MainPage { component: import { MainPage } from src/MainPage, authRequired: true, } route LoginRoute { path: /login, to: LoginPage } page LoginPage { component: import { LoginPage } from src/LoginPage, } route SignupRoute { path: /signup, to: SignupPage } page SignupPage { component: import { SignupPage } from src/SignupPage, }关键点说明app块配置了应用名称与auth这里将userEntity指向User认证方法为usernameAndPassword未登录访问受保护页时重定向到/loginMainPage设置了authRequired: true未登录用户会被拦下路由/、/login、/signup分别指向三个页面组件。然后在schema.prisma中定义实体model User { id Int id default(autoincrement()) tasks Task[] } // We defined a Task entity on which well enable CRUD later on model Task { id Int id default(autoincrement()) description String isDone Boolean userId Int user User relation(fields: [userId], references: [id]) }运行wasp db migrate-dev创建数据库并执行迁移。2. 为 Task 实体添加 CRUD在main.wasp中加入如下crud声明// ... crud Tasks { entity: Task, operations: { getAll: {}, create: { overrideFn: import { createTask } from src/tasks, }, }, }注意这里只启用了getAll和create意味着只会生成这两个操作。同时我们用overrideFn覆盖了create的默认实现create操作将不再由框架生成而是改用src/tasks.{js,ts}中的createTask函数。3. 编写自定义 create 操作为什么要自定义create因为我们希望新建的任务能关联到创建它的那个用户。Automatic CRUD 目前默认不知道这种业务关联详见 CRUD 的未来 与 默认实现所以需要覆盖。src/tasks.jsJavaScript 版本import { HttpError } from wasp/server export const createTask async (args, context) { if (!context.user) { throw new HttpError(401, User not authenticated.) } const { description, isDone } args const { Task } context.entities return await Task.create({ data: { description, isDone, // Connect the task to the user that is creating it user: { connect: { id: context.user.id, }, }, }, }) }src/tasks.tsTypeScript 版本import { type Tasks } from wasp/server/crud import { type Task } from wasp/entities import { HttpError } from wasp/server type CreateTaskInput { description: string; isDone: boolean } export const createTask: Tasks.CreateActionCreateTaskInput, Task async ( args, context ) { if (!context.user) { throw new HttpError(401, User not authenticated.) } const { description, isDone } args const { Task } context.entities return await Task.create({ data: { description, isDone, // Connect the task to the user that is creating it user: { connect: { id: context.user.id, }, }, }, }) }关于 TypeScript 类型的补充说明这也是理解整个覆盖机制的关键Wasp 会根据main.wasp中的 CRUD 声明自动生成Tasks.CreateAction类型用它来标注覆盖函数的实现Tasks.CreateAction与 Wasp 为 Queries 和 Actions 生成类型的机制完全一致标注后 TypeScript 能推断context对象的类型而两个泛型参数分别指定 Action 的输入与输出更完整的类型支持说明见 API 参考中的 Defining the overrides。4. 在客户端使用生成的 CRUD 操作在src/MainPage.jsxJavaScript 版本中import { Tasks } from wasp/client/crud import { useState } from react export const MainPage () { const { data: tasks, isLoading, error } Tasks.getAll.useQuery() const createTask Tasks.create.useAction() const [taskDescription, setTaskDescription] useState() function handleCreateTask() { createTask({ description: taskDescription, isDone: false }) setTaskDescription() } if (isLoading) return divLoading.../div if (error) return divError: {error.message}/div return ( div style{{ fontSize: 1.5rem, display: grid, placeContent: center, height: 100vh, }} div input value{taskDescription} onChange{(e) setTaskDescription(e.target.value)} / button onClick{handleCreateTask}Create task/button /div ul {tasks.map((task) ( li key{task.id}{task.description}/li ))} /ul /div ) }src/MainPage.tsxTypeScript 版本得益于全栈类型安全所有载荷类型自动推断import { Tasks } from wasp/client/crud import { useState } from react export const MainPage () { // Thanks to full-stack type safety, all payload types are inferred // automatically const { data: tasks, isLoading, error } Tasks.getAll.useQuery() const createTask Tasks.create.useAction() const [taskDescription, setTaskDescription] useState() function handleCreateTask() { createTask({ description: taskDescription, isDone: false }) setTaskDescription() } if (isLoading) return divLoading.../div if (error) return divError: {error.message}/div return ( div style{{ fontSize: 1.5rem, display: grid, placeContent: center, height: 100vh, }} div input value{taskDescription} onChange{(e) setTaskDescription(e.target.value)} / button onClick{handleCreateTask}Create task/button /div ul {tasks.map((task) ( li key{task.id}{task.description}/li ))} /ul /div ) }这里有两个值得注意的 APITasks.getAll.useQuery()返回{ data, isLoading, error }是 React Query 风格的 hooks与 Wasp 普通 Query 在客户端的使用方式一致Tasks.create.useAction()返回一个可直接调用的 Action 函数调用后执行后端create。登录与注册页面使用 Wasp 的 Auth UI 组件即可src/LoginPage.jsx/src/LoginPage.tsximport { LoginForm } from wasp/client/auth import { Link } from react-router-dom export function LoginPage() { return ( div style{{ display: grid, placeContent: center, }} LoginForm / div Link to/signupCreate an account/Link /div /div ) }src/SignupPage.jsx/src/SignupPage.tsximport { SignupForm } from wasp/client/auth export function SignupPage() { return ( div style{{ display: grid, placeContent: center, }} SignupForm / /div ) }到这里就完成了。运行wasp start即可看到应用先出现登录/注册页登录后会看到任务列表和新建任务的表单。从源码看 CRUD 的生成机制了解声明如何被编译成实际代码有助于你在排查问题和扩展功能时心中有数。以当前仓库的 waspc 实现为例整条链路如下1. 声明的内部表示waspc/src/Wasp/AppSpec/Crud.hs 定义了Crud记录包含entity :: Ref Entity目标实体引用和operations :: CrudOperationsCrudOperations中get/getAll/create/update/delete均为可选字段每个操作对应CrudOperationOptions其字段正是isPublic :: Maybe Bool与overrideFn :: Maybe ExtImport——与文档中声明语法一一对应。2. 生成 JSON 配置waspc/src/Wasp/Generator/Crud.hs 的getCrudOperationJson把每个启用的操作转换成生成器模板所需的 JSON其中isPublic . fromMaybe False (AS.Crud.isPublic options)明确说明未指定时isPublic默认值为False。同时它还会带上实体的entityUpper大写实体名与entityLower小写实体名用于模板插值。3. 路由生成waspc/src/Wasp/Generator/Crud/Routes.hs 定义了每个操作的 HTTP 路由片段Get - get、GetAll - get-all、Create - create、Update - update、Delete - delete完整路径为crud/{crud名}/{操作名}例如crud/tasks/get-all。这一点在 waspc/tests/Generator/CrudTest.hs 的单元测试中也有断言例如GetAll . mkOperationJson get-all crud/tasks/get-all NotPublic。4. Express 路由注册waspc/data/Generator/templates/server/src/routes/crud/_crud.ts 为每个操作注册了 Express 路由处理器全部使用POST请求并通过createQuery/createAction中间件包装——这说明 CRUD 操作在底层就是 Wasp 的 Queries 和 Actions。若应用开启了认证路由还会挂载auth中间件。5. 默认实现的渲染最核心的模板是 waspc/data/Generator/templates/server/src/crud/_operations.ts。它展示了默认实现与覆盖的完整逻辑若某操作没有overrideFn模板会生成默认实现若操作不是公开的先调用throwIfNotAuthenticated(context)做登录校验开启认证时若context.user为空则抛出createInvalidCredentialsError()若操作配置了overrideFn则直接用用户导入的importIdentifier引用替换默认实现最终getAllFn/getFn/createFn/updateFn/deleteFn作为 Express 路由处理函数导出并注入entities对象const entities { { crud.entityUpper }: prisma.{ crud.entityLower } }。6. 客户端 SDK 与类型客户端 API 由 waspc/data/Generator/templates/sdk/wasp/client/crud/_crud.ts 生成每个操作对外暴露query/useQueryQuery 类或action/useActionAction 类底层调用createQuery/createAction。服务端类型Tasks.GetAllQuery、Tasks.CreateAction等则由 waspc/data/Generator/templates/sdk/wasp/server/crud/_operationTypes.ts 生成其输入/输出类型与 Prisma 的WhereUniqueInput、CreateInput、UpdateInput类型对齐。CRUD 的未来当前 CRUD 操作对它所实现的业务逻辑了解非常有限它不知道任务应该关联到创建它的用户这类业务规则——这正是上面示例中必须覆盖create操作的原因它不了解授权规则例如用户不能为其他用户创建任务。Wasp 未来将引入基于角色的授权role-based authorization并计划让 CRUD 操作感知授权规则它没有输入校验与清洗能力例如无法保证任务描述非空。CRUD 操作是快速搭建后端的机制但它能提供的开箱即用能力取决于它从 Wasp 应用中能获取多少信息——应用提供的信息越充分CRUD 就越强大。Wasp 团队计划持续支持并发展 CRUD让它成为创建后端最简单的方式相关进展可关注 wasp-lang/wasp 仓库中的 issue #1253文中不展开外部链接。API 参考CRUD 声明建立在已有的实体声明之上。下面用两个示例完整探索其 API一个依赖默认选项的基础声明一个使用额外选项与覆盖的复杂声明。声明 CRUD 并使用默认选项为名为Task的实体创建 CRUD 操作像这样写crud Tasks { // crud name here is Tasks entity: Task, operations: { get: {}, getAll: {}, create: {}, update: {}, delete: {}, }, }Wasp 会提供如下默认实现与 waspc/data/Generator/templates/server/src/crud/_operations.ts 中渲染的默认逻辑一致get—— 基于id字段返回单个实体Wasp 使用 Prisma schema 中标记id的字段作为 id 字段// ... // Wasp uses the field marked with id in Prisma schema as the id field. return Task.findUnique({ where: { id: args.id } })getAll—— 返回全部实体若操作非公开Wasp 会校验请求是否来自已认证用户// ... // If the operation is not public, Wasp checks if an authenticated user // is making the request. return Task.findMany()create—— 创建新实体// ... return Task.create({ data: args.data })update—— 更新已有实体// ... // Wasp uses the field marked with id in Prisma schema as the id field. return Task.update({ where: { id: args.id }, data: args.data })delete—— 删除已有实体// ... // Wasp uses the field marked with id in Prisma schema as the id field. return Task.delete({ where: { id: args.id } })TypeScript 项目中的声明与默认实现完全相同模板生成的类型为RegisteredGetQuery/RegisteredGetAllQuery/RegisteredCreateAction/RegisteredUpdateAction/RegisteredDeleteAction。当前限制默认的create和update实现会保存客户端发送的全部数据这并不总是理想行为——例如客户端本不应能修改实体中的所有字段。未来 Wasp 计划为 Action 输入增加校验只保存用户被允许修改的数据。目前的解决方案是提供覆盖函数使用overrideFn选项替换默认实现并自行编写校验逻辑。声明 CRUD 并使用全部可用选项一个更复杂的 CRUD 声明示例crud Tasks { // crud name here is Tasks entity: Task, operations: { getAll: { isPublic: true, // optional, defaults to false }, get: {}, create: { overrideFn: import { createTask } from src/tasks, // optional }, update: {}, }, }CRUD 声明包含以下字段entity: Entity必填 要应用 CRUD 操作的实体。operations: { [operationName]: CrudOperationOptions }必填 要生成的操作集合。键为操作名值为操作配置。operationName的合法取值getAllgetcreateupdatedeleteCrudOperationOptions可包含以下字段isPublic: bool—— 操作是否公开。公开则无需认证即可访问非公开则仅限已认证用户。默认为false。overrideFn: ExtImport—— 可选覆盖实现的 Node.js 导入语句。定义覆盖函数Overrides与 Actions 和 Queries 类似你可以在 JavaScript/TypeScript 文件中定义覆盖实现。覆盖函数接收两个参数args操作的参数即客户端发送的数据。context包含发起请求的user以及entities对象其中包含被操作的实体。TypeScript 项目中可以通过wasp/server/crud导入{crud 名}来获取每个可覆盖函数的类型可用类型包括{crud name}.GetAllQuery{crud name}.GetQuery{crud name}.CreateAction{crud name}.UpdateAction{crud name}.DeleteAction如果 CRUD 名为Tasks导入方式如下import { type Tasks } from wasp/server/crud // Each of the types is a generic type, so you can use it like this: export const getAllOverride: Tasks.GetAllQueryInput, Output async ( args, context ) { // ... }每个类型都是泛型两个类型参数分别对应输入与输出类型。使用示例见前文 为 Task 实体添加 CRUD 一节。在客户端代码中使用 CRUD 操作在客户端从wasp/client/crud导入{crud 名}对象。例如 CRUD 名为Tasksimport { Tasks } from wasp/client/crud然后即可访问各个操作const { data } Tasks.getAll.useQuery() const { data } Tasks.get.useQuery({ id: 1 }) const createAction Tasks.create.useAction() const updateAction Tasks.update.useAction() const deleteAction Tasks.delete.useAction()注意get、getAll生成的是 QueryuseQuerycreate、update、delete生成的是 ActionuseAction。所有 CRUD 操作在底层都由 Queries 和 Actions 实现这一点在 waspc/data/Generator/templates/server/src/routes/crud/_crud.ts 中通过createQuery/createAction中间件可以清晰看到因此它们天然继承这些基础能力的所有特性例如自动的 SuperJSON 序列化、TypeScript 下的全栈类型安全等。你也可以在仓库的 crud 生成器测试 中看到对公开操作、覆盖操作、路由路径等行为的自动化验证。【免费下载链接】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

相关资讯

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

较早相关资讯

最新相关资讯

CubeSandbox 多机集群部署指南:控制面 + 计算节点架构与调度配置 2026/9/16 16:43:24

CubeSandbox 多机集群部署指南:控制面 + 计算节点架构与调度配置

CubeSandbox 多机集群部署指南:控制面 计算节点架构与调度配置 【免费下载链接】CubeSandbox Instant, Concurrent, Secure & Lightweight Sandbox for AI Agents. 项目地址: https://gitcode.com/GitHub_Trending/cu/CubeSandbox 本指南讲解如何把单机…

阅读更多 →
Java本地生成Mapbox Sprite图集与JSON资源 2026/9/16 16:43:24

Java本地生成Mapbox Sprite图集与JSON资源

简介:本资源是一个基于Java与Spring Boot开发的本地化Mapbox精灵图片(Sprite)生成与拆分工具,面向地图前端开发者、后端Java工程师及需要离线定制地图图标的GIS应用人员。它解决了在无网络或高安全要求环境下无法调用Mapbox在线Sp…

阅读更多 →
GeoLibre Chrome扩展开发解析:从扫描网页到组装地图URL 2026/9/16 16:43:24

GeoLibre Chrome扩展开发解析:从扫描网页到组装地图URL

GeoLibre Chrome扩展开发解析:从扫描网页到组装地图URL 【免费下载链接】GeoLibre A lightweight, cloud-native GIS platform for visualizing, exploring, and analyzing geospatial data. It runs in the web browser, on the desktop, on mobile, and inside Ju…

阅读更多 →
基于YOLOv5的单目测距技术:原理与工程实现 2026/9/16 16:43:24

基于YOLOv5的单目测距技术:原理与工程实现

简介:基于YOLOv5的单目测距系统源码包,面向计算机视觉方向的毕业设计开发者,尤其适合需要快速搭建目标检测与距离估计一体化项目的学生。压缩包共78个文件,整体约215.3MB,文件类型覆盖28个Python脚本、26个YAML配置、6…

阅读更多 →
Karpenter E2E 测试体系全解析:GitHub Actions 触发机制、AWS 环境搭建与测试套件架构 2026/9/16 16:43:24

Karpenter E2E 测试体系全解析:GitHub Actions 触发机制、AWS 环境搭建与测试套件架构

Karpenter E2E 测试体系全解析:GitHub Actions 触发机制、AWS 环境搭建与测试套件架构 【免费下载链接】karpenter-provider-aws Karpenter is a Kubernetes Node Autoscaler built for flexibility, performance, and simplicity. 项目地址: https://gitcode.com…

阅读更多 →
国产芯片替代实测:从MCU到电源接口,ST/TI/NXP替换边界全记录 2026/9/16 16:40:23

国产芯片替代实测:从MCU到电源接口,ST/TI/NXP替换边界全记录

1. 为什么突然要测国产替代:一场被动选型引发的系统性验证1.1 触发这次测试的真实背景2021年底到2022年那段时间,做硬件的朋友应该都有记忆——ST、TI、NXP的交期动不动就拉到52周以上,ST有时候报出来直接是“无货”。我们当时有一款工业控制…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

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

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