十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

如何把应用后端从 Convex 迁移到 SpacetimeDB

如何把应用后端从 Convex 迁移到 SpacetimeDB 如何把应用后端从 Convex 迁移到 SpacetimeDB【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDB这篇文章面向正在把应用后端从 Convex 迁到 SpacetimeDB 的开发者。两个系统都包含数据库状态、服务端逻辑、生成客户端和实时更新但编程模型不同Convex 中客户端通过query读数据、通过mutation写数据SpacetimeDB 中客户端通过**订阅subscription**读取表的实时数据通过reducer修改状态。迁移的核心就是围绕这个差异重写数据模型和服务端函数。按文档给出的流程走完你可以得到一个发布到 Maincloud 或自托管宿主上的 SpacetimeDB 数据库并让客户端通过生成的绑定完成连接、订阅与调用。准备条件安装 CLI、登录并创建项目迁移工作开始前按 Getting Started 完成三件事安装spacetimeCLI。安装说明在 Getting Started 页面中CLI 用于管理数据库与部署。登录 SpacetimeDBspacetime login命令会打开浏览器让你通过 GitHub 或 Google 登录。即使跳过这一步后续需要登录的命令如spacetime publish也会在运行时要求登录。创建 SpacetimeDB 项目。交互式入口是spacetime dev它会引导你输入项目名、选择项目路径和客户端类型之后进入带热重载的开发模式见 spacetime dev 文档。如果你更喜欢手动创建用spacetime init --lang typescript --project-path ./my-project my-project cd my-project服务器语言可选 TypeScript、C#、Rust、C命令形式相同只是--lang取值不同。TypeScript 模板通常把模块代码放在spacetimedb/src/index.ts文档标注spacetime dev目前是不稳定命令后续可能变化。如果需要本地跑一个独立服务器调试可以在安装 CLI 后执行spacetime start。服务器默认监听 3000 端口可通过--listen-addr修改standalone 模式在前台运行且不支持 SSL。第一步盘点 Convex 后端并给每个函数分类在动代码之前把 Convex 应用里的东西列全见 Migrating from Convexconvex/schema.ts中的全部表UI 组件用到的 query用户动作触发的 mutation用于第三方 API、邮件、支付、搜索等副作用的 action用于 webhook 或公开端点的 HTTP action定时函数与 cron 任务认证假设尤其是用户 ID 字段和 provider 特定的 claimsFile Storage 的使用情况Components 与共享后端包。然后按每个函数实际做了什么分类这决定了它在 SpacetimeDB 中的去向Convex 概念SpacetimeDB 去向QueryView 函数、订阅或 SQL 查询MutationReducer事务性、确定性ActionProcedure需要副作用时只改数据库状态的应改为 reducerHTTP ActionHTTP handler定时函数 / CronSchedule tableFile Storage二进制列或外部存储引用Components子模块或独立模块/数据库术语对照表中还有几个迁移时高频的概念defineTable对应table()defineSchema对应schema(...)或语言特定的模块 schema文档 IDIdtable对应主键、唯一键或Identity_id对应显式命名的主键列常用id_creationTime需要显式的Timestamp列并从ctx.timestamp赋值useQuery对应订阅加客户端缓存或 view 订阅useMutation对应生成的 reducer 调用npx convex dev对应spacetime devnpx convex deploy对应spacetime publish。第二步把文档式表重设计为关系行Convex 的文档是 JSON 风格对象SpacetimeDB 的表是带类型的关系行。文档明确建议不要机械地把每个嵌套文档塞进一张宽表而是按访问模式拆表。例如 Convex 中这样定义的users文档users: defineTable({ name: v.string(), avatarUrl: v.optional(v.string()), preferences: v.object({ theme: v.string(), emailNotifications: v.boolean(), }), lastSeenAt: v.number(), });可以拆成 SpacetimeDB 的两张表import { schema, table, t } from spacetimedb/server; const user table( { name: user, public: true }, { identity: t.identity().primaryKey(), name: t.string(), avatarUrl: t.option(t.string()), lastSeenAt: t.timestamp().index(btree), } ); const userPreference table( { name: user_preference, public: true }, { identity: t.identity().primaryKey(), theme: t.string(), emailNotifications: t.bool(), } ); const spacetimeDb schema({ user, userPreference }); export default spacetimeDb;文档给出的判断规则是如果两个字段被读取或更新的频率不同就考虑拆成不同的表——这能减少订阅带宽保持热数据体积小。表的结构细节列类型、约束、索引、可见性、调度见 Tables。第三步把 mutation 改写成 reducerConvex mutation 通常变成 SpacetimeDB reducer把校验、授权和写库都放进 reducer。文档用发送消息这个场景做了对照。Convex 侧export const send mutation({ args: { channelId: v.id(channels), body: v.string() }, handler: async (ctx, args) { const identity await ctx.auth.getUserIdentity(); if (identity null) throw new Error(Not signed in); return await ctx.db.insert(messages, { channelId: args.channelId, author: identity.subject, body: args.body, createdAt: Date.now(), }); }, });对应的 SpacetimeDB 写法import { schema, table, t, SenderError } from spacetimedb/server; const message table( { name: message, public: true }, { id: t.u64().primaryKey().autoInc(), channelId: t.u64().index(btree), author: t.identity().index(btree), body: t.string(), createdAt: t.timestamp().index(btree), } ); const spacetimeDb schema({ message }); export default spacetimeDb; export const sendMessage spacetimeDb.reducer( { channelId: t.u64(), body: t.string() }, (ctx, { channelId, body }) { if (body.trim() ) { throw new SenderError(Message body cannot be empty); } ctx.db.message.insert({ id: 0n, channelId, author: ctx.sender, body, createdAt: ctx.timestamp, }); } );改写时注意两个差异reducer 用ctx.sender作为已认证调用者不要接受客户端传入的用户身份参数reducer 不返回插入的行 ID。客户端通过订阅message表感知新行。如果客户端需要一次性的成功/失败通知用 SDK 的按调用 reducer 结果回调如果其他订阅者需要临时事件在 reducer 里向事件表event table插入一行。Reducer 的完整定义见 Reducers。第四步把 query 替换为订阅和 viewConvex query 通常混着两种用途为 UI 取实时行以及从一张或多张表算出服务端结果。SpacetimeDB 里分开处理取实时行客户端直接订阅表或 SQL 查询从客户端缓存渲染。例如 Convex 中按 channel 取最新 100 条消息的 query可以改成订阅SELECT * FROM message WHERE channelId ... ORDER BY createdAt DESC LIMIT 100前提是表上有支持该查询形状的channelId和createdAt索引。算计算型读模型定义 view。view 尤其适合 join 和派生行import type { Timestamp } from spacetimedb; const messageWithAuthor t.row(MessageWithAuthor, { id: t.u64(), channelId: t.u64(), authorName: t.string(), body: t.string(), createdAt: t.timestamp(), }); export const messagesWithAuthors spacetimeDb.anonymousView( { name: messages_with_authors, public: true }, t.array(messageWithAuthor), ctx { const rows: Array{ id: bigint; channelId: bigint; authorName: string; body: string; createdAt: Timestamp; } []; for (const msg of ctx.db.message.iter()) { const author ctx.db.user.identity.find(msg.author); if (author) { rows.push({ id: msg.id, channelId: msg.channelId, authorName: author.name, body: msg.body, createdAt: msg.createdAt, }); } } return rows; } );有一个明确限制view 目前不接受任意客户端参数。如果一个 Convex query 带参数文档给出三条路客户端用参数化的 SQL/query-builder 表达式订阅、把参数建模为订阅表数据的一部分或者让 view 的结果可以由客户端订阅侧过滤。订阅机制见 Subscriptionsview 见 Views。第五步把 action 拆成 reducer 与 procedureConvex action 既调第三方服务也能调 query/mutationSpacetimeDB 里要把确定性的数据库变更留在 reducer把副作用工作移到 procedure。文档给出的工作流模式例如支付处理一个 reducer 记录请求的操作并校验调用者一个 procedure 执行外部 API 调用procedure 用withTx提交结果对应的数据库变更或者如果该操作可以表达为常规状态转换就调用一个 reducer。约束是reducer 里不做网络工作因为 reducer 必须是确定性和事务性的。需要 procedure 能力的场景出站 HTTP 等见 Procedures。Convex 的 HTTP action 则对应 SpacetimeDB 的 HTTP handler用于 webhook、OAuth 回调、上传回调和公开 HTTP API。如果调用方是 SpacetimeDB 客户端、需要副作用但不需要 HTTP 路由用 procedure。第六步迁移认证、定时任务、文件与共享组件认证与用户。Convex 的ctx.auth.getUserIdentity()对应 SpacetimeDB 函数上下文中的调用者Identityctx.sender。把用户行以Identity作为键存储const user table( { name: user, public: true }, { identity: t.identity().primaryKey(), displayName: t.string(), createdAt: t.timestamp(), } ); export const createProfile spacetimeDb.reducer( { displayName: t.string() }, (ctx, { displayName }) { ctx.db.user.insert({ identity: ctx.sender, displayName, createdAt: ctx.timestamp, }); } );需要 provider 特定数据时检查认证上下文里可用的 OIDC claimsSpacetimeDB 支持包括 SpacetimeAuth、Auth0、Clerk 在内的 OIDC provider。授权检查应在 reducer、view、procedure 和连接生命周期 reducer 中基于ctx.sender和 claims 完成详见 Authentication。定时任务。Convex 的定时函数和 cron 对应 schedule table表中插入的行会让某个 reducer 或 procedure 在特定时间或按间隔运行。确定性的数据库维护用 scheduled reducer需要外部 I/O 的任务发邮件、调第三方 API用 scheduled procedure。文件。Convex File Storage 对应两种模式小型二进制数据直接放进表列需要参与事务、随行实时更新时大文件放对象存储SpacetimeDB 表里保留元数据、归属和 URL。浏览器上传的常见流程客户端走既有上传流程把文件传到对象存储 → 客户端调用 reducer 登记元数据和归属 → 其他客户端通过订阅收到元数据。Components 与共享后端代码。SpacetimeDB 侧要显式建模边界可用子模块放可复用的隔离系统需要运维隔离时用独立模块/数据库纯逻辑留在普通语言模块或包里集成边界通过 procedure、HTTP handler 和收窄的表 schema 表达。不要把共享代码直接放开到无关表的访问权限——保留 Convex 组件当初的接口边界。验证与发布生成绑定、更新客户端并 publish迁移完成后的落地路径对应迁移文档的 Checklist用spacetime generate或spacetime dev生成客户端绑定。spacetime dev会启动本地服务器、创建数据库、构建并发布模块监听源码变化并在保存时自动重建重发布如果配置了客户端开发命令写在项目根目录spacetime.json的dev.run字段例如run: npm run dev它还会顺带跑客户端开发服务器。该命令在含spacetimedb/目录的已有项目中会跳过初始化直接进入开发模式。更新客户端连接数据库、订阅表或 view、从客户端缓存渲染、调用生成的 reducer/procedure 方法。发布。在模块目录通常是spacetimedb/执行spacetime login spacetime publish DATABASE_NAMEspacetime publish会自动构建模块无需单独执行spacetime build但spacetime build可以单独用来编译并校验模块结构创建数据库、上传安装模块、运行init生命周期 reducer如果定义了并开始接受客户端连接。发布成功后 SpacetimeDB 会输出数据库的 identity务必保存后续管理操作要用它。两个有破坏性的选项要特别留意见 spacetime publishspacetime publish --break-clients DATABASE_NAME用于无法自动迁移的破坏性 schema 变更会打断未适配新 schema 的现有客户端spacetime publish DATABASE_NAME --delete-data重置数据库并永久删除全部数据迁移已上线应用时不要误用。完整命令参数见 CLI reference。常见迁移陷阱迁移文档总结了五个高频问题都在上文流程中有对应动作期望 reducer 返回数据reducer 只做事务性状态变更。UI 需要的数据要建模成行并订阅临时消息用事件表必须请求/响应式的流程用 procedure。文档照搬成宽行直接的文档到行转换会产生更新过于频繁的大行制造不必要的订阅流量。按访问模式和更新频率拆表。从客户端传用户 ID 做授权不要信任客户端传来的用户 ID 参数用上下文中的调用者Identity再查用户行。用 procedure 做常规写reducer 是默认写路径除非需要出站 HTTP 等 procedure 专属能力。忘记索引Convex 的withIndex(...)让索引使用显式可见SpacetimeDB 需要同样的设计步骤——为你的应用依赖的查找和订阅定义索引。迁移完成判断迁移完成的标志就是迁移文档 Checklist 全部打勾表已按访问模式定义、文档 ID 换成显式主键/唯一键/Identity列、时间戳列显式化、索引齐备、mutation 全部转为 reducer返回值换成订阅/事件表/view/procedure 返回、query 转为订阅或 view、副作用 action 转为 procedure、HTTP action 转为 HTTP handler、定时任务进入 schedule table、授权改为ctx.sender与 OIDC claims 检查、客户端绑定已生成、客户端已改为连接、订阅、缓存渲染、调用生成的 reducer/procedure最后spacetime publish成功并拿到数据库 identity。此后如需继续深入按术语表中的对应项分别阅读 Tables、Reducers、Views、Procedures、HTTP handlers 和 Subscriptions。【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDB创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表