concepts

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

SpacetimeDB Core Concepts

SpacetimeDB 核心概念

SpacetimeDB, which is being renamed Spacetime, is the whole backend: a relational database that is also a server. It lets you upload application logic directly into the database as modules, eliminating the traditional web/game server layer entirely. Rust, C#, and C++ modules compile to WebAssembly, while TypeScript modules run on V8.

SpacetimeDB(正在更名为 Spacetime)就是完整的后端:它既是关系型数据库,也是服务器。它允许将应用程序逻辑直接作为模块上传到数据库中,彻底消除传统的 Web/游戏服务器层。Rust、C# 和 C++ 模块会编译为 WebAssembly,而 TypeScript 模块则运行在 V8 上。

Current Capabilities

当前能力

This describes Spacetime 2.x. The API changed substantially from 1.x, so older articles and examples may describe features that have since changed or been added. Each line links to the docs.
  • Architecture. Spacetime is the whole backend: server logic runs inside the database as a module, and clients connect to it directly over WebSocket, with no separate API server, cache, or sync layer. (Zen of Spacetime)
  • App types. It is used for games, such as BitCraft Online, and for web apps such as chat, collaboration tools, dashboards, and SaaS. The TypeScript SDK has quickstarts for React, Next.js, Vue, Svelte, and more. (React quickstart)
  • Storage. Tiered storage keeps hot data in memory and colder data on disk. On Maincloud, tables can also be backed by object storage, so a single database can store an effectively unbounded amount of data. (How does Spacetime scale?)
  • Scaling. Each database runs its transactions one at a time on a single thread. One app can span many databases with asynchronous inter-database communication, and Maincloud replicates each database. (How does Spacetime scale?)
  • Durability. Transactions are ACID with serializable isolation. By default, clients see a transaction's results, including the acknowledgment of their own writes, only after it is durable. (Confirmed reads)
  • Module languages. TypeScript, Rust, C#, and C++. TypeScript modules run on V8 and can use npm packages, which are bundled when you publish; Rust, C#, and C++ modules compile to WebAssembly. (Language support)
  • Composing modules. TypeScript and C# modules can mount other modules as submodules, each under its own namespace, to reuse logic such as authentication, rate limiting, or email. Official and community modules are listed at spacetimedb.com/modules. (Modules)
  • External APIs. Reducers are deterministic and make no network calls. Procedures make outbound HTTP requests, with a 30-second default timeout and a 180-second maximum. (Procedures)
  • Webhooks. HTTP handlers serve inbound HTTP routes from the module, so it can receive webhooks and verify their signatures. (HTTP handlers)
  • Secrets. Modules read secrets from environment variables, which are stored privately and never sent to clients. Reducers, procedures, and views can read them, and other clients never see a reducer's arguments. (Environment variables)
  • Time and randomness. Reducers get the call's timestamp from
    ctx.timestamp
    and a seeded random number generator from their context. (Reducer context)
  • Reads. Clients subscribe to SQL queries and to views. Views are read functions in module code that can take typed arguments, like Convex queries. (Views)
  • Access control. Tables are private by default. Views expose exactly the rows and columns each client should see, including per-caller views filtered by the caller's identity. (Access permissions)
  • Auth. Spacetime works with any OIDC provider, such as Auth0, Clerk, Better Auth, or Firebase Auth. SpacetimeAuth is a first-party provider with magic links, social logins, and user and role management. (Authentication)
  • Scheduled work. Schedule tables run reducers or procedures at a specific time or on a repeating interval. In TypeScript, the Cron module adds calendar schedules with time zones. (Schedule tables, Cron module)
  • Ephemeral events. Event tables broadcast rows to subscribers without storing them, for things like typing indicators. (Event tables)
  • Files. Files can be stored as bytes in table columns, or a procedure can store large files in S3 or a similar service and keep a reference in a table. In TypeScript, the Files module handles uploads, listing and download routes. (File storage, Files module)
  • SQL and tools. Spacetime speaks the PostgreSQL wire protocol, so PostgreSQL drivers,
    psql
    , and GUI clients can connect. (PostgreSQL wire protocol)
  • Dashboard. On Maincloud, each database's dashboard on spacetimedb.com lets you browse, filter, edit, delete, and export table rows, call reducers, and view logs and metrics. (Maincloud dashboard)
  • Schema changes. Compatible changes, such as adding tables or columns with defaults, migrate automatically when you republish, and incremental migrations handle the rest. (Automatic migrations)
  • Development.
    spacetime dev
    rebuilds and republishes the module and regenerates client bindings on every save. (spacetime dev)
  • Testing. A unit-test harness runs reducers, procedures, and views against the real datastore and controls the clock, the random seed, and HTTP responses. (Unit testing)
  • Coming from Convex. A Convex mutation maps to a reducer, an action to a procedure, an HTTP action to an HTTP handler, a query to a view, and a component to a module that you mount as a submodule. (Migrating from Convex)
  • Hosting. Maincloud handles replication and backups, and its Pro and higher plans include an uptime SLA. Plans, prices, and a usage calculator are on the pricing page. Anyone can self-host with
    spacetime start
    or Docker, and the Business Source License 1.1 lets an application use one instance in production, as long as you don't offer Spacetime itself as a database service. (Self-hosting)
  • Beta labels. Features labeled beta may still change their API. BitCraft Online runs entirely on Spacetime in production.
For questions about older articles, benchmarks, or past reviews, see Common misconceptions.

以下内容描述 Spacetime 2.x。其 API 相较 1.x 发生了重大变化,因此较旧的文章和示例可能描述的是此后已经变更或新增的功能。每一行均链接到相应文档。
  • 架构。 Spacetime 就是完整的后端:服务器逻辑作为模块在数据库内部运行,客户端通过 WebSocket 直接连接到它,无需单独的 API 服务器、缓存或同步层。(Spacetime 之禅)
  • 应用类型。 它可用于游戏,例如 BitCraft Online,也可用于聊天、协作工具、仪表盘和 SaaS 等 Web 应用。TypeScript SDK 提供针对 React、Next.js、Vue、Svelte 等框架的快速入门指南。(React 快速入门)
  • 存储。 分层存储将热数据保留在内存中,并将较冷的数据保存在磁盘上。在 Maincloud 上,表还可以由对象存储提供支持,因此单个数据库实际上可以存储近乎无限量的数据。(Spacetime 如何扩缩容?)
  • 扩缩容。 每个数据库在单个线程上一次只运行一个事务。一个应用可以跨越多个数据库,并通过异步的数据库间通信进行协作,而 Maincloud 会复制每个数据库。(Spacetime 如何扩缩容?)
  • 持久性。 事务符合 ACID,并采用可串行化隔离级别。默认情况下,客户端只有在事务持久化之后才能看到其结果,包括对其自身写入操作的确认。(确认读取)
  • 模块语言。 TypeScript、Rust、C# 和 C++。TypeScript 模块运行在 V8 上,可以使用 npm 包;发布时这些包会被打包。Rust、C# 和 C++ 模块则编译为 WebAssembly。(语言支持)
  • 模块组合。 TypeScript 和 C# 模块可以将其他模块作为子模块挂载,每个子模块都位于自己的 namespace 下,从而复用身份验证、速率限制或电子邮件等逻辑。官方模块和社区模块列于 spacetimedb.com/modules。(Modules)
  • 外部 API。 Reducer 是确定性的,不会发起网络调用。Procedure 会发出对外 HTTP 请求,默认超时时间为 30 秒,最长为 180 秒。(Procedures)
  • Webhooks。 HTTP handler 可为模块提供入站 HTTP 路由,使模块能够接收 webhook 并验证其签名。(HTTP handlers)
  • Secrets。 模块从环境变量读取 Secrets;这些变量会被私密存储,且绝不会发送给客户端。Reducer、Procedure 和 View 均可读取它们,而其他客户端永远看不到 Reducer 的参数。(Environment variables)
  • 时间与随机性。 Reducer 可通过
    ctx.timestamp
    获取调用时间戳,并从其 context 中获取带种子的随机数生成器。(Reducer context)
  • 读取。 客户端可订阅 SQL query 和 View。View 是模块代码中的只读函数,可以接受类型化参数,类似 Convex query。(Views)
  • 访问控制。 表默认是私有的。View 会精确暴露每个客户端应看到的行和列,包括按调用者身份过滤的 per-caller View。(访问权限)
  • 身份验证。 Spacetime 可与任意 OIDC provider 配合使用,例如 Auth0、Clerk、Better Auth 或 Firebase Auth。SpacetimeAuth 是第一方 provider,支持 magic link、社交登录以及用户和角色管理。(身份验证)
  • 定时任务。 Schedule table 可在特定时间或按重复间隔运行 Reducer 或 Procedure。在 TypeScript 中,Cron module 可添加带时区的日历调度。(Schedule tables、Cron module)
  • 临时事件。 Event table 会将行广播给订阅者而不存储这些行,适用于输入状态指示器等场景。(Event tables)
  • 文件。 文件可以字节形式存储在表列中,或者由 Procedure 将大文件存储在 S3 或类似服务中,并在表中保留引用。在 TypeScript 中,Files module 负责处理上传、列表和下载路由。(文件存储、Files module)
  • SQL 与工具。 Spacetime 支持 PostgreSQL wire protocol,因此 PostgreSQL driver、
    psql
    和 GUI client 均可连接。(PostgreSQL wire protocol)
  • 仪表盘。 在 Maincloud 上,每个数据库在 spacetimedb.com 上的 dashboard 可让你浏览、筛选、编辑、删除和导出表行,调用 Reducer,以及查看日志和指标。(Maincloud dashboard)
  • Schema 变更。 兼容变更,例如添加表或带默认值的列,会在重新发布时自动迁移,其余变更则由增量迁移处理。(自动迁移)
  • 开发。
    spacetime dev
    会在每次保存时重新构建并重新发布模块,并重新生成客户端 bindings。(spacetime dev)
  • 测试。 单元测试 harness 会在真实 datastore 上运行 Reducer、Procedure 和 View,并控制时钟、随机种子及 HTTP 响应。(单元测试)
  • 从 Convex 迁移。 Convex mutation 对应 Reducer,action 对应 Procedure,HTTP action 对应 HTTP handler,query 对应 View,而 component 则对应可作为子模块挂载的 module。(从 Convex 迁移)
  • 托管。 Maincloud 处理复制和备份,其 Pro 及更高套餐提供 uptime SLA。套餐、价格和用量计算器位于定价页面。任何人都可以使用
    spacetime start
    或 Docker 进行自托管;Business Source License 1.1 允许应用程序在生产环境中使用一个实例,前提是你不将 Spacetime 本身作为数据库服务提供。(自托管)
  • Beta 标签。 标为 beta 的功能其 API 仍可能发生变化。BitCraft Online 完全基于 Spacetime 运行于生产环境。
如需了解旧文章、基准测试或过往评测中的问题,请参阅常见误区。

Critical Rules

关键规则

  1. Reducers are transactional. They do not return data to callers. Use subscriptions to read data.
  2. Reducers must be deterministic. Do not use filesystem, network, external clocks, or external random sources in reducers. Use the reducer context (
    ctx
    ) for SpacetimeDB-provided timestamp and deterministic random values.
  3. Read data via tables/subscriptions, not reducer return values. Clients get data through subscribed queries.
  4. Auto-increment IDs are not sequential. Gaps are normal, do not use for ordering. Use timestamps or explicit sequence columns.
  5. ctx.sender
    is the authenticated principal.
    Never trust identity passed as arguments.

  1. Reducer 是事务性的。 它们不会向调用方返回数据。请使用 subscription 读取数据。
  2. Reducer 必须具备确定性。 不要在 Reducer 中使用文件系统、网络、外部时钟或外部随机源。请使用 Reducer context(
    ctx
    )获取 SpacetimeDB 提供的时间戳和确定性随机值。
  3. 通过表或 subscription 读取数据,而不是依赖 Reducer 返回值。客户端通过订阅的 query 获取数据。
  4. 自增 ID 并非连续。 出现间隔是正常现象,不要用其进行排序。请使用时间戳或显式的 sequence 列。
  5. ctx.sender
    是经过身份验证的 principal。
    绝不要信任作为参数传入的身份信息。

Feature Implementation Checklist

功能实现清单

  1. Backend: Define table(s) to store the data
  2. Backend: Define reducer(s) to mutate the data
  3. Client: Subscribe to the table(s)
  4. Client: Call the reducer(s) from UI
  5. Client: Render the data from the table(s)

  1. 后端: 定义用于存储数据的表
  2. 后端: 定义用于修改数据的 Reducer
  3. 客户端: 订阅相应的表
  4. 客户端: 从 UI 调用相应的 Reducer
  5. 客户端: 渲染相应表中的数据

Debugging Checklist

调试清单

  1. Is SpacetimeDB server running? (
    spacetime start
    )
  2. Is the module published? (
    spacetime publish
    )
  3. Are client bindings generated? (
    spacetime generate
    )
  4. Check server logs for errors (
    spacetime logs <db-name>
    )
  5. Is the reducer actually being called from the client?

  1. SpacetimeDB server 是否正在运行?(
    spacetime start
    )
  2. module 是否已发布?(
    spacetime publish
    )
  3. 客户端 bindings 是否已生成?(
    spacetime generate
    )
  4. 检查 server 日志中的错误(
    spacetime logs <db-name>
    )
  5. 客户端是否确实调用了 Reducer?

Tables

表

  • Private tables (default): Only accessible by reducers and the database owner.
  • Public tables: Exposed for client read access through subscriptions. Writes still require reducers.
Organize data by access pattern, not by entity:
Player          PlayerState         PlayerStats
id         <--  player_id           player_id
name            position_x          total_kills
                position_y          total_deaths
                velocity_x          play_time
  • 私有表(默认):仅 Reducer 和 database owner 可访问。
  • 公共表:通过 subscription 向客户端开放读取访问。写入仍须通过 Reducer 完成。
应按访问模式而非实体组织数据:
Player          PlayerState         PlayerStats
id         <--  player_id           player_id
name            position_x          total_kills
                position_y          total_deaths
                velocity_x          play_time

Reducers

Reducer

Reducers are transactional functions that modify database state. They run atomically, cannot interact with the outside world, and do not return data to callers. See the language-specific server skills for syntax.
Reducer 是修改数据库状态的事务函数。它们以原子方式运行,不能与外部世界交互,也不会向调用方返回数据。有关语法,请参阅特定语言的 server skill。

Event Tables

Event Table

Event tables broadcast reducer-specific data to clients. Rows are never stored in the client cache (
count()
returns 0,
iter()
yields nothing); only
onInsert
callbacks fire.
Event table 将 Reducer 专属数据广播给客户端。行绝不会存储在客户端缓存中(
count()
返回 0,
iter()
不产生任何内容);只会触发
onInsert
回调。

Subscriptions

Subscription

Subscriptions replicate database rows to clients in real-time.
  1. Subscribe: Register SQL queries describing needed data
  2. Receive initial data: All matching rows are sent immediately
  3. Receive updates: Real-time updates when subscribed rows change
  4. React to changes: Use callbacks (
    onInsert
    ,
    onDelete
    ,
    onUpdate
    )
Best practices:
  • Group subscriptions by lifetime
  • Subscribe before unsubscribing when updating subscriptions
  • Avoid overlapping queries
  • Use indexes for efficient queries
Subscription 会将数据库行实时复制到客户端。
  1. 订阅: 注册描述所需数据的 SQL query
  2. 接收初始数据: 所有匹配的行会立即发送
  3. 接收更新: 订阅的行发生变化时实时更新
  4. 响应变化: 使用回调(
    onInsert
    、
    onDelete
    、
    onUpdate
    )
最佳实践:
  • 按生命周期对 subscription 进行分组
  • 更新 subscription 时,先订阅再取消旧订阅
  • 避免重叠的 query
  • 使用索引提高 query 效率

Modules

Module

Modules contain application logic that runs inside the database.
  • Tables: Define the data schema
  • Reducers: Define callable functions that modify state
  • Event Tables: Broadcast reducer-specific data to clients
  • Views: Read-only functions that expose computed subsets of data to clients
  • Procedures: (Unstable) Functions that can have side effects (HTTP requests,
    ctx.withTx
    )
Server-side modules can be written in: Rust, C#, TypeScript, C++
Lifecycle: Write → Compile → Publish (
spacetime publish
) → Hot-swap (republish without disconnecting clients)
Module 包含在数据库内部运行的应用程序逻辑。
  • Table: 定义数据 schema
  • Reducer: 定义可调用的状态修改函数
  • Event Table: 向客户端广播 Reducer 专属数据
  • View: 只读函数,用于向客户端公开数据的计算子集
  • Procedure:(不稳定)可产生副作用的函数,例如发起 HTTP 请求或调用
    ctx.withTx
服务端 module 可以使用以下语言编写:Rust、C#、TypeScript、C++
生命周期:编写 → 编译 → 发布(
spacetime publish
)→ 热替换(重新发布而不断开客户端)

Reusable Modules and Configuration

可复用 Module 与配置

TypeScript and C# support same-language submodule composition. A namespace has a language accessor and a canonical database name; SQL, CLI calls, and HTTP calls use the canonical name. Changing the canonical namespace drops its tables and their data, so pin it when renaming an accessor. TypeScript supports nested submodules; C# composition requires 2.13+ with .NET 10/C# 14, and nested or repeated C# mounts require 2.14+. See the respective server skills for registration and helper-context rules.
Database environment values are read-only strings, including secrets, supplied at publish time, not process environment variables. Keys are exact and case-sensitive: declare every key module code reads; undeclared reads fail even if a value is stored. Required declarations need values before initialization/migration; optional declarations permit absence. Publishing preserves unspecified values; environment-only publishing changes configuration without replacing the module. Transactional reads share a snapshot. In TypeScript/C#, named submodules cannot declare their own environment or read the root environment through host-dispatched entry points; ordinary helper calls retain the caller's access. C# dependencies registered in
public
instead contribute to the root schema. Do not return or log secrets; private storage does not prevent module code from exposing them.
TypeScript 和 C# 支持使用相同语言的 submodule 组合。每个 namespace 都具有一个 language accessor 和一个 canonical database name;SQL、CLI 调用和 HTTP 调用均使用 canonical name。修改 canonical namespace 会删除其表及数据,因此在重命名 accessor 时应将其固定。TypeScript 支持 nested submodule;C# composition 要求使用 2.13+、.NET 10/C# 14,而 nested 或 repeated C# mount 要求使用 2.14+。有关注册及 helper-context 规则,请参阅相应的 server skill。
数据库 environment value 是只读字符串,包括 secrets,在 publish 时提供,而不是 process environment variable。键名必须精确且区分大小写:必须声明模块代码读取的每个键;即使已存储某个值,未声明的读取也会失败。必需声明在初始化或 migration 前必须具有值;可选声明允许不存在。发布会保留未指定的值;仅发布 environment 内容可在不替换 module 的情况下更改配置。事务性读取共享同一 snapshot。在 TypeScript/C# 中,named submodule 不能声明自己的 environment,也不能通过 host-dispatched entry point 读取 root environment;普通 helper call 会保留调用者的访问权限。注册在
public
中的 C# dependency 会被纳入 root schema。不要返回或记录 secrets;private storage 并不能阻止 module 代码将其暴露。

Views with Arguments and Module Tests

带参数的 View 与 Module 测试

Views can accept arguments in 2.13+ (Rust, TypeScript, C#), queried as
SELECT * FROM view_name(42)
. Use explicit queries instead of subscribe-all on that connection; subscribe-all skips these views. See server skills for definition restrictions and client skills for query/cache rules.
Rust and TypeScript provide in-memory module unit-test harnesses in 2.12+: run native
cargo test
or Vitest with the module-test plugin. These use the real datastore but do not simulate clients, subscriptions, RLS enforcement, lifecycle events, or timer execution. Test those host behaviors against a running database.
从 2.13+ 开始,View 可以接受参数(Rust、TypeScript、C#),查询方式为
SELECT * FROM view_name(42)
。请在该连接上使用显式 query,而不是 subscribe-all;subscribe-all 会跳过这些 View。有关定义限制,请参阅 server skill;有关 query/cache 规则,请参阅 client skill。
从 2.12+ 开始,Rust 和 TypeScript 提供内存中的 module 单元测试 harness:使用 module-test plugin 运行原生
cargo test
或 Vitest。这些测试使用真实 datastore,但不会模拟客户端、subscription、RLS enforcement、lifecycle event 或 timer execution。请针对运行中的 database 测试这些 host behavior。

Inter-Database Communication

数据库间通信

Reducers can't make network requests, but a reducer can call a reducer in another database on the same server through an outbox table (async IDC). The reducer inserts a row, and after its transaction commits, the row is delivered as a reducer call to the target database exactly once, in order with other calls through the same outbox to the same database. The sending reducer doesn't wait, and the two databases don't commit together: the receiving reducer runs later in its own transaction and can fail. An optional
on_result
reducer in the sending database receives the result, so it can undo or record the work.
Use it to split an application into databases that scale and deploy independently, such as a lobby that sends players to separate game world databases. Only Rust and TypeScript modules can declare outbox tables. See the language-specific server skills for syntax.
Reducer 无法发起网络请求,但可以通过 outbox table 调用同一服务器上另一个数据库中的 Reducer(异步 IDC)。Reducer 插入一行;其事务提交后,该行会作为一次 Reducer 调用投递到目标数据库,并且相对于通过同一 outbox 发往同一数据库的其他调用,恰好投递一次且顺序有序。发送方 Reducer 不会等待,两个数据库也不会共同提交:接收方 Reducer 稍后在自己的事务中运行,并且可能失败。发送数据库中的可选
on_result
Reducer 可接收结果,以便撤销或记录该工作。
可以使用此机制将应用拆分为可独立扩缩容和部署的多个数据库,例如由 lobby 向独立的 game world 数据库发送玩家。只有 Rust 和 TypeScript module 可以声明 outbox table。有关语法,请参阅特定语言的 server skill。

Identity

身份

  • Identity: A long-lived, globally unique identifier for a user.
  • ConnectionId: Identifies a specific client connection.
  • Always use
    ctx.sender
    /
    ctx.Sender
    /
    ctx.sender()
    for authorization.
SpacetimeDB works with many OIDC providers, including SpacetimeAuth (built-in), Auth0, Clerk, Keycloak, Google, and GitHub.
  • Identity:用户的长期全局唯一标识符。
  • ConnectionId:标识特定的客户端连接。
  • 授权时始终使用
    ctx.sender
    /
    ctx.Sender
    /
    ctx.sender()
    。
SpacetimeDB 可与多种 OIDC provider 配合使用,包括 SpacetimeAuth(内置)、Auth0、Clerk、Keycloak、Google 和 GitHub。