SpacetimeDB 数据库模块开发指南:使用spacetime dev开启热重载交互式开发
【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDB
导读
本文基于 SpacetimeDB 官方文档 docs/docs/00200-core-concepts/00100-databases/00200-spacetime-dev.md,系统讲解如何通过spacetime dev命令创建并迭代 SpacetimeDB 数据库模块(database module)。spacetime dev是一个交互式开发命令:它自动引导你完成项目初始化、启动本地服务器、创建数据库、构建并发布模块,并在你每次保存代码时自动重新构建、重新发布,配合客户端开发服务器实现真正的"保存即热重载"。读完本文,你将掌握spacetime dev的完整交互流程、客户端类型(React / 模板 / 纯服务端)的选择逻辑、spacetime.json配置文件的用法,以及手动spacetime init+spacetime publish的替代工作流。
注意:
spacetime dev目前仍是一个不稳定(unstable)命令,其行为在未来版本中可能发生变化。
环境准备:安装 SpacetimeDB CLI
开始之前,需要先安装 SpacetimeDB CLI。CLI 是整个开发流程的入口,负责项目初始化(spacetime init)、构建(spacetime build)、发布(spacetime publish)、数据库交互(spacetime call、spacetime sql、spacetime logs)以及本文的主角spacetime dev。
安装完成后,可在任意终端执行:
spacetime dev注意:如果当前目录中不存在 SpacetimeDB 项目,
spacetime dev会进入交互式初始化流程;如果已存在包含spacetimedb/目录的项目,则会直接进入开发模式,连接数据库并监听文件变化。
spacetime dev的首次初始化交互流程
在没有现成项目的目录下运行spacetime dev,CLI 会通过 init.rs 中的交互逻辑引导你完成三步设置。从源码结构看(dev.rs),该流程由init::exec_with_options驱动,最终调用init_from_template生成项目骨架。
Step 1:输入项目名称
输入项目名称,例如my-project。该名称将作为数据库名称的默认值传入初始化选项(project_name_default与database_name_default),也会成为项目目录的默认名称。
Step 2:选择项目路径
选择项目文件的创建位置,默认值为./<project-name>,即当前目录下以项目名命名的子目录。
Step 3:选择客户端类型
根据你的开发目标选择客户端形态:
| 选项 | 说明 |
|---|---|
| React | 创建带 TypeScript 服务端模块的 React 全栈 Web 应用(推荐用于 Web 应用开发),客户端与服务端均预配置热重载 |
| Use Template | 从内置模板中选择,或通过 GitHub 仓库(owner/repo)或 git URL 克隆已有项目 |
| None | 仅创建服务端模块,不含任何客户端代码,随后需要选择服务端语言 |
选择None时,还需进一步选择服务端语言:
- TypeScript—— 服务端模块使用 TypeScript
- Rust—— 服务端模块使用 Rust
- C#—— 服务端模块使用 C#
- C++—— 服务端模块使用 C++
无论选择哪种客户端类型,服务端代码都会统一创建在项目的spacetimedb/子目录中。这也是spacetime dev对模板项目的硬性要求——源码注释明确写道:"All server templates must have their server code inspacetimedb/directory",spacetime dev默认即从<project-path>/spacetimedb查找模块(见 dev.rs 中spacetimedb_dir的默认解析逻辑)。
提示:
spacetime dev也可通过--template参数(-t)直接指定模板 ID 或 GitHub 仓库,从而跳过交互式模板选择;但如果当前目录已存在 SpacetimeDB 项目,--template会被忽略并打印警告("Warning: --template option is ignored because a SpacetimeDB project already exists.")。
客户端类型详解
React:一键生成全栈 Web 应用
选择 React 会创建完整的前后端应用骨架,包含:
- TypeScript 服务端模块(位于
spacetimedb/) - 集成 SpacetimeDB 客户端 SDK 的 React 前端
- 客户端与服务端均预配置好的热重载
从模板定义(templates/react-ts/.template.json)可以看到,该模板基于 React + Vite + TypeScript 构建,其package.json的scripts中提供了dev(vite 开发服务器)、generate(生成客户端绑定)、spacetime:publish(发布到 maincloud)等脚本,spacetime dev会自动复用这些脚本。
Use Template:内置模板与 GitHub 克隆
spacetime dev支持从内置模板列表中选择项目形态。下表列出了官方文档列举的 8 个内置模板,其元数据均可在仓库的 templates 目录下对应的.template.json文件中核实:
| 模板 ID | 说明 | 源码位置 |
|---|---|---|
basic-ts | 基础 TypeScript 客户端与服务端桩代码 | templates/basic-ts |
basic-cs | 基础 C# 客户端与服务端桩代码 | templates/basic-cs |
basic-rs | 基础 Rust 客户端与服务端桩代码 | templates/basic-rs |
basic-cpp | 基础 C++ 服务端桩代码 | templates/basic-cpp |
react-ts | React Web 应用 + TypeScript 服务端 | templates/react-ts |
chat-console-rs | 完整的 Rust 聊天实现(服务端 + 控制台客户端) | templates/chat-console-rs |
chat-console-cs | 完整的 C# 聊天实现 | templates/chat-console-cs |
chat-react-ts | 完整的 TypeScript 聊天实现(React 前端) | templates/chat-react-ts |
此外,你还可以输入一个 GitHub 仓库(owner/repo形式)或 git URL,CLI 会通过clone_github_template(见 init.rs)将远程项目克隆为本地项目。
以basic-ts模板为例,其生成的模块代码位于spacetimedb/src/index.ts,内容是一个最小的可运行模块——定义了一张名为person的表(字段name)以及init(模块发布时调用)、onConnect(客户端连接时调用)、onDisconnect(客户端断开时调用)生命周期回调和addreducer(插入一条 person 记录):
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 }); } );这个最小模块就是后续学习 Tables(表)、Reducers(操作回调)与 Procedures(存储过程)的最佳起点。
None:仅服务端模块
选择 None 将跳过所有客户端代码的生成,只创建一个服务端模块。根据服务端语言的不同,模板分别为:
- TypeScript——
spacetimedb/package.json+spacetimedb/src/index.ts - Rust——
spacetimedb/Cargo.toml+spacetimedb/src/lib.rs - C#——
spacetimedb/StdbModule.csproj+spacetimedb/Lib.cs - C++——
spacetimedb/CMakeLists.txt+spacetimedb/src/lib.cpp
服务端代码同样统一放在项目根目录下的spacetimedb/子目录中。
初始化后的项目结构
根据服务端语言的不同,初始化完成后生成的项目结构如下。
TypeScript
my-project/ ├── spacetimedb/ # Server module code │ ├── package.json │ ├── tsconfig.json │ └── src/ │ └── index.ts ├── src/ # Client code │ └── module_bindings/ # Generated client bindings ├── package.json ├── tsconfig.json ├── spacetime.json # SpacetimeDB configuration └── README.mdC#
my-project/ ├── spacetimedb/ # Server module code │ ├── StdbModule.csproj │ └── Lib.cs ├── module_bindings/ # Generated client bindings ├── client.csproj ├── Program.cs ├── spacetime.json # SpacetimeDB configuration └── README.mdRust
my-project/ ├── spacetimedb/ # Server module code │ ├── Cargo.toml │ └── src/ │ └── lib.rs ├── src/ # Client code │ └── module_bindings/ # Generated client bindings ├── Cargo.toml ├── spacetime.json # SpacetimeDB configuration ├── .gitignore └── README.mdC++
my-project/ ├── spacetimedb/ # Server module code (C++) │ ├── CMakeLists.txt │ └── src/ │ └── lib.cpp └── README.md注意:C++ 模板目前仅提供服务端模块(不包含客户端),且对 C++ 模块版本有一定限制,使用时请以 CLI 实际生成结果为准。
初始化完成后的自动执行流程
完成上述设置后,spacetime dev会自动按顺序执行以下操作:
- 启动本地 SpacetimeDB 服务器(当目标服务器为
local时) - 创建一个新的数据库
- 构建并发布你的模块到该数据库
- 监听源文件变化
- 保存时自动重新构建并重新发布
- 运行客户端开发服务器(如果配置了)
根据官方文档,开发环境的数据库默认位于https://maincloud.spacetimedb.com。需要说明的是:spacetime dev的目标服务器遵循"CLI 参数 > 默认服务器 > maincloud"的解析顺序(见 dev.rs 中resolved_server的解析);若未登录且目标为 maincloud,CLI 会提示先登录,若拒绝登录则回退使用本地服务器。
从源码看,这个"自动发布循环"的实现位于 dev.rs 中的generate_build_and_publish函数,其内部依次完成三件事:
- 构建:调用
tasks::build编译模块(Rust/C# 编译为 WASM,TypeScript 打包为 V8 JavaScript 引擎可执行的 bundle); - 生成绑定:调用
generate::exec_from_entries为客户端生成module_bindings; - 发布:对每个 publish 配置项调用
publish::exec_from_entry将模块发布到目标数据库。
而在文件监听侧,CLI 使用notify库递归监听所有 watch 目录,并实现了 300ms 的去抖窗口(debounce):文件变更事件会先被合并,停止变动 300ms 后才触发一次重新构建,避免保存大文件或批量操作时产生重复发布。
日志流自动跟随:进入开发模式后,CLI 还会自动启动日志流,通过GET /v1/database/{identity}/logs接口实时拉取目标数据库的日志并输出到终端(start_log_stream/stream_logs),因此你在终端里就能直接看到 reducer 的log输出,无需手动执行spacetime logs。
已有项目:直接进入开发模式
如果当前目录已存在 SpacetimeDB 项目(包含spacetimedb/目录或spacetime.json配置文件),spacetime dev会跳过初始化流程直接进入开发模式,连接已有数据库并开始监听文件变化。此时如果没有配置任何 publish 目标(spacetime.json中无database字段或children),CLI 会引导你选择目标数据库——可选"创建随机命名的新数据库"或"从已有数据库中选择"(后者通过FuzzySelect交互选择,见select_database)。
提示:一旦选定数据库,CLI 会提示
Use spacetime dev <database> to skip this question next time,并将数据库名持久化到spacetime.local.json(见 dev.rs 中的create_local_spacetime_config_if_missing),下次运行即可跳过数据库选择。
配置客户端开发服务器:spacetime.json
spacetime dev可以自动并行启动客户端的开发服务器。客户端启动命令的解析优先级为:
- CLI 标志:
spacetime dev --run "yarn dev"(最高优先级,覆盖配置文件) - 配置文件:
spacetime.json中的dev.run字段 - 自动检测:从项目文件自动探测(
package.json的dev脚本、Cargo.toml、.csproj) --server-only:显式禁用客户端启动
配置文件示例
在项目根目录的spacetime.json中加入dev字段:
{ "dev": { "run": "npm run dev" } }从源码看,dev字段由DevConfig结构体解析(见 spacetime_config.rs),其唯一的键run即客户端启动命令,且该配置是仅根级(root-only)的——不会被继承到children子数据库目标。
客户端命令的自动检测
如果项目尚未配置dev.run,CLI 会根据项目类型自动探测客户端命令并写回spacetime.json(detect_and_save_client_command)。自动检测逻辑(detect_client_command,见 spacetime_config.rs)如下:
| 项目特征 | 检测到的命令 |
|---|---|
package.json存在且包含scripts.dev | 依据锁文件选择包管理器:pnpm-lock.yaml→pnpm dev;yarn.lock→yarn dev;bun.lock/bun.lockb→bun dev;否则默认npm run dev |
Cargo.toml存在 | cargo run |
存在.csproj文件 | dotnet run |
当你使用带客户端模板的spacetime init创建项目时,CLI 会基于项目类型自动在spacetime.json中写入默认客户端命令,例如 React 模板会写入npm run dev(对应 Vite 开发服务器脚本)。
客户端环境变量注入
客户端进程启动时,CLI 会为它注入两个环境变量(start_client_process):
SPACETIMEDB_DB_NAME—— 目标数据库名称SPACETIMEDB_HOST—— 目标服务器主机 URL
对于 TypeScript 客户端,CLI 还会自动将这些值以多种框架前缀(SPACETIMEDB_、VITE_SPACETIMEDB_、NEXT_PUBLIC_SPACETIMEDB_、REACT_APP_SPACETIMEDB_、EXPO_PUBLIC_SPACETIMEDB_、PUBLIC_SPACETIMEDB_)写入项目根目录的.env.local文件(upsert_env_db_names_and_hosts),因此无论你使用 Vite、Next.js、Create React App、Expo 还是 SvelteKit,客户端代码都能通过约定的环境变量找到数据库并建立连接。
客户端进程的 stdout/stderr 直接继承当前终端,方便观察前端输出;如果客户端命令启动后立即失败(例如命令不存在或依赖缺失),spacetime dev会快速报错退出;客户端进程中途退出则只打印提示,文件监听与模块重发布仍会继续。
spacetime dev的命令行参数速查
spacetime dev的完整参数定义见 dev.rs,核心参数如下:
| 参数 | 说明 |
|---|---|
<database>(位置参数) | 目标数据库名称/身份,缺省时交互式提示选择。旧版--database标志已废弃,仅用于向后兼容 |
--project-path <PATH> | 项目目录路径,默认.(当前目录) |
--module-path <PATH> | 服务端模块路径(相对当前目录),默认<project-path>/spacetimedb |
--module-bindings-path <PATH> | 客户端绑定输出目录(相对项目目录),默认src/module_bindings |
--client-lang <LANG> | 生成客户端绑定的语言(typescript / csharp / rust / unrealcpp 等),缺省时从项目自动检测 |
--server <SERVER> | 目标服务器昵称、主机名或 URL(对应spacetime已配置的服务器) |
-t, --template <TEMPLATE> | 初始化用的模板 ID 或 GitHub 仓库(owner/repo或 URL) |
--run <COMMAND> | 客户端开发服务器启动命令(覆盖spacetime.json的dev.run) |
--server-only | 仅运行服务端(模块),不启动客户端 |
--no-config | 忽略spacetime.json配置(此时客户端命令仅靠自动检测,且不会写回配置文件) |
--env <ENV> | 配置文件分层环境名(dev / staging 等),spacetime dev默认dev |
--skip-publish | 跳过发布步骤(仅构建 + 生成绑定) |
--skip-generate | 跳过绑定生成步骤 |
--clear-database | 数据库清理模式(默认 OnConflict,即仅在冲突时处理) |
--dotnet-version <MAJOR> | C# 模块使用的 .NET 主版本号(如 8 或 10) |
--native-aot | C# 项目使用 NativeAOT-LLVM 构建(.NET 10 下恒使用,此标志被忽略) |
-y, --yes | 跳过所有交互式确认 |
冲突约束:当
spacetime.json中包含 publish 目标(database或children)时,不允许同时使用--module-path;当配置中包含 generate 目标时,不允许同时使用--module-path、--project-path或--module-bindings-path,否则 CLI 会直接报错退出(见 dev.rs 中的校验逻辑)。
文件监听与忽略规则
开发模式的可靠运行离不开精细的文件过滤。从源码看,监听器采用三层过滤(should_ignore_path):
- 常驻忽略目录(无论 gitignore 如何配置都忽略):
.git、.hg、.svn、target(Rust)、build/bin/obj(C++/.NET)、node_modules、dist、.next、.nuxt、.output(JS/TS 构建产物)、__pycache__、.venv、venv、.vs、.idea; - 强制监听例外:
.env.local与spacetime.*.local.json即使被 gitignore 忽略也仍然触发重新构建(因为数据库连接信息可能在这些文件中被修改); - gitignore 规则:合并全局 gitignore、项目级与模块级的
.gitignore后按规则过滤。
这套规则保证了node_modules、dist等海量文件的改动不会引发无意义的重复发布。
替代方案:手动创建项目(spacetime init+spacetime publish)
如果你希望更精细地控制开发流程,可以手动创建项目,再使用标准的构建与发布工作流。手动流程分为两步:spacetime init生成项目骨架,spacetime publish构建并发布模块。
使用spacetime init创建项目
TypeScript
spacetime init --lang typescript --project-path ./my-project my-project cd my-project创建内容:为 SpacetimeDB 配置好的package.json、含示例模块的src/index.ts、示例表与 reducer 定义。
C#
spacetime init --lang csharp --project-path ./my-project my-project cd my-project创建内容:为 SpacetimeDB 配置好的StdbModule.csproj、含示例模块的Lib.cs、示例表与 reducer 定义。
Rust
spacetime init --lang rust --project-path ./my-project my-project cd my-project创建内容:为 SpacetimeDB 配置好的Cargo.toml、含示例模块的src/lib.rs、示例表与 reducer 定义。
C++
spacetime init --lang cpp --project-path ./my-project my-project cd my-project创建内容:为 SpacetimeDB 配置好的CMakeLists.txt、含示例模块的src/lib.cpp、示例表与 reducer 定义。
非交互模式下,必须提供
--template或--lang之一,否则 CLI 会报错 "Either --template or --lang must be provided in non-interactive mode"。
构建与发布模块
手动工作流的关键命令记录在配套文档 docs/docs/00200-core-concepts/00100-databases/00300-spacetime-publish.md 中:
- 进入模块目录(通常是项目内的
spacetimedb/),执行构建:
spacetime buildRust 与 C# 模块会编译为 WebAssembly(WASM),TypeScript 模块会打包为 V8 JavaScript 引擎可执行的形式。
- 登录认证(发布到云端前需要):
spacetime login- 发布模块(创建新数据库或更新已有数据库):
spacetime publish <DATABASE_NAME>spacetime publish会自动完成:构建模块(若未构建)→ 创建/定位数据库 → 上传并安装模块 → 运行init生命周期 reducer(若定义了)→ 开始接受客户端连接。发布成功后控制台会输出数据库身份(database identity),请妥善保存,后续管理操作需要用到。
- 更新已有数据库:再次执行
spacetime publish <DATABASE_NAME>即可,SpacetimeDB 会尝试自动迁移 schema 并原子化切换新模块,同时保持已有客户端连接不断开; - 破坏性变更:当 schema 变更无法自动迁移时,使用
spacetime publish --break-clients <DATABASE_NAME>(会断开未升级到新 schema 的客户端,请谨慎使用); - 清空数据:
spacetime publish <DATABASE_NAME> --delete-data会永久删除数据库内全部数据。
提示:直接执行
spacetime publish时无需先单独执行spacetime build,publish 会自动完成构建。
spacetime init生成项目后,spacetime dev仍可接管
手动创建的项目(含spacetime.json)同样可以直接运行spacetime dev进入热重载模式:spacetime dev会读取spacetime.json中的 publish 目标(database、server、module-path)与dev.run客户端命令,复用同一套构建、生成绑定、发布与监听循环。二者的工作流是互补的——init+publish适合明确的发布操作,dev适合高频迭代开发。
spacetime.json配置结构补充
除了dev字段,理解spacetime.json的完整结构有助于手动编排开发流程。从 spacetime_config.rs 中的SpacetimeConfig定义看,其核心字段包括:
{ "database": "my-database", "server": "local", "module-path": "./server", "dev": { "run": "pnpm dev" }, "generate": [ { "language": "typescript", "out-dir": "./src/module_bindings" } ], "children": [ { "database": "region-1" }, { "database": "region-2", "module-path": "./region-server" } ] }字段语义:
| 字段 | 语义 |
|---|---|
database | 目标数据库名称;配合children可实现多数据库发布 |
server | 目标服务器昵称(如local、maincloud) |
module-path | 服务端模块路径,默认./spacetimedb |
dev.run | 客户端开发服务器命令(仅根级生效,不向下继承) |
generate | 客户端绑定生成条目(如语言与输出目录),按数据库生效 |
children | 子数据库目标,可继承父级未设置的字段(dev、generate、children不参与继承) |
此外,spacetime dev支持配置文件分层:除了根级spacetime.json,还会加载spacetime.<env>.json与spacetime.local.json覆盖层(--env默认dev)。spacetime.local.json通常被 gitignore 忽略,用于存放个人化的开发数据库选择(例如{"database": "my-app-123456"}),避免污染提交到版本库的主配置。
下一步学习路径
创建并运行你的第一个数据库模块之后,建议按官方文档的顺序继续深入:
- 掌握数据模型:阅读 Tables、Reducers 与 Procedures;
- 深入了解生产发布:阅读 spacetime publish 指南(构建、发布、自动迁移、
--break-clients与--delete-data); - 在仓库内查看各语言模板的真实实现,作为自己项目的参考起点:TypeScript 模板见 templates/basic-ts,Rust 模板见 templates/basic-rs,C# 模板见 templates/basic-cs,C++ 模板见 templates/basic-cpp;
- 想深入
spacetime dev的底层实现,可直接阅读 crates/cli/src/subcommands/dev.rs(构建/发布/监听循环)与 crates/cli/src/spacetime_config.rs(配置解析与客户端命令检测)。
【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDB
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考