ARTICLE DETAIL

资讯详情

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

SpacetimeDB Node.js 模板实战:用 nodejs-ts 模板在 5 分钟内跑通全栈应用

SpacetimeDB Node.js 模板实战:用 nodejs-ts 模板在 5 分钟内跑通全栈应用 SpacetimeDB Node.js 模板实战用 nodejs-ts 模板在 5 分钟内跑通全栈应用【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDB本文基于仓库中的 templates/nodejs-ts/README.md完整讲解 SpacetimeDB 官方 Node.js 模板nodejs-ts的搭建与运行流程如何一条命令启动包含 TypeScript 服务端模块与 Node.js 客户端的项目、person表与add/sayHello两个 reducer 的完整定义、通过 CLI 调用 reducer 与 SQL 查询数据、客户端DbConnection.builder()的连接与订阅机制以及 Node.js 环境下的 WebSocket 兼容、环境变量配置与 token 文件持久化等实战细节。读完本文你可以独立创建、修改并调试一个 SpacetimeDB 全栈 Node.js 应用并理解其自动生成的类型绑定。前置条件开始之前需要满足两个条件已安装Node.js 18已安装SpacetimeDB CLI仓库文档要求先安装 CLI 再继续。Node.js 版本的差异会影响 WebSocket 实现选择见 Node.js 特有注意事项 一节Node.js 22 自带原生 WebSocket 支持而 Node.js 18–21 会由 SDK 自动回退到undici包——该包已作为devDependencies包含在模板中无需额外安装。一条命令创建项目运行以下命令即可创建一个同时包含 SpacetimeDB 模块与 Node.js 客户端的新项目spacetime dev --template nodejs-tsspacetime dev会一次性完成四件事启动本地 SpacetimeDB 服务器发布你的模块生成 TypeScript 客户端绑定即src/module_bindings/目录下的类型与类运行 Node.js 客户端。整个流程在一个前台进程中完成CtrlC即可退出。项目结构解析模板生成的项目同时包含服务端与客户端代码。以当前仓库 templates/nodejs-ts 的实际布局为准my-spacetime-app/ ├── spacetimedb/ # 你的 SpacetimeDB 模块服务端 │ ├── src/ │ │ └── index.ts # 服务端逻辑表、reducer、事件回调 │ ├── package.json # 模块自身依赖build/publish 均走 spacetime CLI │ └── tsconfig.json # SpacetimeDB 要求的模块编译配置 ├── src/ │ ├── main.ts # Node.js 客户端脚本 │ └── module_bindings/ # 自动生成的类型与连接类 │ ├── index.ts │ ├── types.ts │ ├── person_table.ts │ ├── add_reducer.ts │ ├── say_hello_reducer.ts │ └── types/ │ ├── procedures.ts │ └── reducers.ts ├── package.json └── tsconfig.json分工非常清晰编辑 spacetimedb/src/index.ts 来添加表和 reducers编辑 src/main.ts 来构建 Node.js 客户端。src/module_bindings/是自动生成的文件头均标注 THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE WILL NOT BE SAVED修改数据模型应回到模块源码而非绑定文件。两个值得留意的配置文件客户端 package.json中的脚本与undici依赖scripts: { dev: esbuild src/main.ts --bundle --platformnode --outfiledist/main.js --formatesm node dist/main.js, start: node dist/main.js, build: esbuild src/main.ts --bundle --platformnode --outfiledist/main.js --formatesm, typecheck: tsc --noEmit, spacetime:generate: spacetime generate --lang typescript --out-dir src/module_bindings --module-path spacetimedb }, devDependencies: { types/node: ^20.0.0, esbuild: ^0.24.0, typescript: ~5.6.2, undici: ^6.19.2 }可以看到客户端用 esbuild 把src/main.ts打成 ESM 再交给node执行spacetime:generate脚本展示了重新生成绑定的完整命令——spacetime generate --lang typescript --out-dir src/module_bindings --module-path spacetimedb即绑定输出到src/module_bindings模块源目录为spacetimedb。客户端 tsconfig.json使用ES2022/NodeNext模块体系并开启strict面向 Node.js 运行时而模块侧 spacetimedb/tsconfig.json 的注释明确说明部分选项target: ESNext、module: ESNext、isolatedModules: true、noEmit: true等是 SpacetimeDB 强制要求、不可修改的因为它要在 WASM 目标下编译。理解表Tables与 Reducers打开 spacetimedb/src/index.ts模板模块的完整代码如下import { schema, table, t } from spacetimedb/server; const spacetimedb schema({ person: table( { public: true }, { name: t.string(), } ), }); export default spacetimedb; export const init spacetimedb.init(_ctx { // Called when the module is initially published }); export const onConnect spacetimedb.clientConnected(_ctx { // Called every time a new client connects }); export const onDisconnect spacetimedb.clientDisconnected(_ctx { // Called every time a client disconnects }); export const add spacetimedb.reducer( { name: t.string() }, (ctx, { name }) { ctx.db.person.insert({ name }); } ); export const sayHello spacetimedb.reducer(ctx { for (const person of ctx.db.person.iter()) { console.info(Hello, ${person.name}!); } console.info(Hello, World!); });核心概念只有两条Tables 存储数据。这里定义了person表属性{ public: true }表示客户端可以订阅列只有一个name字符串。Reducers 是修改数据的函数且是写入数据库的唯一途径。add接收{ name: t.string() }参数并执行ctx.db.person.insert({ name })sayHello无参数遍历ctx.db.person.iter()逐一打招呼最后输出Hello, World!。它的输出是日志而非数据库变更因此要观察效果需要查看服务端日志见下文spacetime logs。文档正文未展开但源码中存在的三件事务钩子也值得一提spacetimedb.init模块首次发布时调用、spacetimedb.clientConnected/spacetimedb.clientDisconnected客户端连接/断开时调用。模板将它们留空作为扩展点。模块侧 spacetimedb/package.json 只有两个脚本build执行spacetime buildpublish执行spacetime publish——模块的编译与发布完全交给 CLI不直接调用 esbuild/tsc。运行客户端spacetime dev会同时拉起服务器和 Node.js 客户端spacetime dev --template nodejs-ts客户端行为是连接 SpacetimeDB、订阅所有表、在人员被添加或删除时实时打印到控制台CtrlC退出。由于spacetime dev是前台进程实际操作中通常再开一个终端窗口执行 CLI 命令下一节来触发数据变化。通过 SpacetimeDB CLI 调用 Reducers在服务运行期间用 CLI 调用 reducer、查询数据所有变更会实时出现在 Node.js 客户端# 添加人员 spacetime call add Alice spacetime call add Bob # 让所有人打招呼结果看服务端日志 spacetime call say_hello # 查询数据库 spacetime sql SELECT * FROM person注意spacetime call say_hello使用的是snake_case的say_hello而模块源码里导出的 reducer 名叫sayHello。这并非笔误从自动生成的绑定 src/module_bindings/index.ts 可以看到注册到 reducer schema 的线上名称就是蛇形命名const reducersSchema __reducers( __reducerSchema(add, AddReducer), __reducerSchema(say_hello, SayHelloReducer) );也就是说 TypeScript 模块中驼峰的导出名在客户端与 CLI 这一侧统一按snake_case暴露调用时须使用say_hello。add只有一个单词两种写法一致。更完整的 CLI 交互示例含查询与日志输出# 调用 add reducer 插入一个人 spacetime call add Charlie # 查询 person 表 spacetime sql SELECT * FROM person # 输出 # name # --- # Alice # Bob # Charlie # 调用 sayHello 让所有人打招呼 spacetime call say_hello # 查看模块日志 spacetime logs # 2025-01-13T12:00:00.000000Z INFO: Hello, Alice! # 2025-01-13T12:00:00.000000Z INFO: Hello, Bob! # 2025-01-13T12:00:00.000000Z INFO: Hello, Charlie! # 2025-01-13T12:00:00.000000Z INFO: Hello, World!理解客户端代码打开 src/main.ts 即可看到模板 Node.js 客户端的完整实现。它使用DbConnection.builder()建立连接、订阅所有表并为 insert/delete 事件注册回调。与浏览器应用最大的不同在于Node.js 把认证 token 存到文件而不是localStorage。关键实现如下完整代码见 templates/nodejs-ts/src/main.tsimport * as fs from fs; import * as path from path; import { fileURLToPath } from url; import { Identity } from spacetimedb; import { DbConnection, ErrorContext, EventContext, } from ./module_bindings/index.js; // 配置优先读环境变量缺省值对应 spacetime dev 的本地端口与模块名 const HOST process.env.SPACETIMEDB_HOST ?? ws://localhost:3000; const DB_NAME process.env.SPACETIMEDB_DB_NAME ?? nodejs-ts; async function main(): Promisevoid { DbConnection.builder() .withUri(HOST) .withDatabaseName(DB_NAME) .withToken(loadToken()) .onConnect(onConnect) .onDisconnect(onDisconnect) .onConnectError(onConnectError) .build(); } function onConnect(conn: DbConnection, identity: Identity, token: string): void { console.log(Connected to SpacetimeDB!); console.log(Identity: ${identity.toHexString().slice(0, 16)}...); saveToken(token); // 保存 token 供后续连接复用 // 订阅所有表 conn .subscriptionBuilder() .onApplied(ctx { const people [...ctx.db.person.iter()]; console.log(\nCurrent people (${people.length}):); for (const person of people) { console.log( - ${person.name}); } console.log(\nPress CtrlC to exit); }) .onError((_ctx, err) { console.error(Subscription error:, err); }) .subscribeToAllTables(); // 注册表变更回调 conn.db.person.onInsert((_ctx: EventContext, person) { console.log([Added] ${person.name}); }); conn.db.person.onDelete((_ctx: EventContext, person) { console.log([Removed] ${person.name}); }); }几个实现要点连接生命周期回调除了文档提到的onConnect模板还挂了onDisconnect区分正常断开与带错误断开和onConnectError连接失败时打印错误并process.exit(1)这是 Node.js 长驻客户端的健壮性保障浏览器页面客户端通常不需要。token 文件持久化token 写入项目根目录下的.spacetimedb-token文件src/的上一级下次启动时通过loadToken()读取并传给.withToken(...)从而复用身份、避免每次连接都重新认证读取或写入失败仅打印警告而不中断连接流程。事件模型subscriptionBuilder().onApplied(...)在订阅建立/应用时触发此时遍历ctx.db.person.iter()打印当前全量人员conn.db.person.onInsert / onDelete则在后续增量变更时逐条触发配合spacetime call add Alice就能看到[Added] Alice的实时输出。自动生成的绑定长什么样src/module_bindings/下的文件由 CLI 生成src/module_bindings/index.ts 头部记录了生成它的 CLI 版本为 2.0.0其结构与模块源码一一对应文件内容index.tsDbConnection/SubscriptionBuilder类、tables查询构建器、reducers访问器以及REMOTE_MODULE含cliVersion与全部表/ reducer schematypes.ts行类型定义如Person t.object(Person, { name: t.string() })person_table.tsperson表的行 schemat.row({ name: t.string() })add_reducer.tsadd的入参 schema{ name: t.string() }say_hello_reducer.tssay_hello的入参 schema空对象{}types/reducers.ts由入参 schema 推导的参数类型AddParams/SayHelloParamsindex.ts中值得注意的两点一是生成的DbConnection通过static builder()工厂绑定REMOTE_MODULE因此客户端拿到的conn.db.person、conn.reducers.add都带完整的行/参数类型二是tables导出为__makeQueryBuilder(...)意味着每个表引用本身也是一个查询构建器。当你修改模块源码后运行 package.json 中的spacetime:generate脚本即可重新生成。Node.js 特有注意事项WebSocket 支持Node.js 22 有原生 WebSocket 支持Node.js 18–21 上 SDK 会自动使用undici包已包含在devDependencies版本^6.19.2无需手动干预。环境变量连接通过SPACETIMEDB_HOST与SPACETIMEDB_DB_NAME两个环境变量配置客户端在 src/main.ts 中的读取逻辑为const HOST process.env.SPACETIMEDB_HOST ?? ws://localhost:3000; const DB_NAME process.env.SPACETIMEDB_DB_NAME ?? nodejs-ts;即不设置时回退到spacetime dev的本地默认地址ws://localhost:3000与模板默认库名nodejs-ts。连接远端实例的完整示例# 通过环境变量配置 SPACETIMEDB_HOSTws://localhost:3000 \ SPACETIMEDB_DB_NAMEmy-app \ npm run startnpm run start执行的是node dist/main.js因此需先执行npm run buildesbuild 打包或npm run dev打包并运行一步完成。退出方式CtrlC停止客户端。小结nodejs-ts模板演示了 SpacetimeDB 全栈开发的最小闭环spacetime dev --template nodejs-ts一条命令完成本地服务器、模块发布、绑定生成与客户端运行服务端只写table与reducer外加可选的init/clientConnected/clientDisconnected钩子客户端则依赖自动生成的DbConnection完成连接、订阅与变更回调token 以文件形式持久化。后续扩展路径把person表换成你的领域模型、在 spacetimedb/src/index.ts 中增加 reducers并在 src/main.ts 中消费这些变更重新生成绑定使用npm run spacetime:generate。若需要完整聊天应用示例可参考仓库中的 templates/chat-react-ts/README.md 模板目录。【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDB创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表