Skip to content
Backend
Skill

/rust-server

SpacetimeDB Rust server module SDK reference. Use when writing tables, reducers, or module logic in Rust.

From plugin
spacetimedb
26k11 skills1 MCP

Claude Code

$ npx -y skills add clockworklabs/spacetimedb --skill rust-server --agent claude-code

SKILL.md

rust-server.SKILL.md
name: rust-server
description: SpacetimeDB Rust server module SDK reference. Use when writing tables, reducers, or module logic in Rust.
license: Apache-2.0
metadata:
  author: clockworklabs
  version: "2.0"
  role: server
  language: rust
  cursor_globs: "**/*.rs"
  cursor_always_apply: true

SpacetimeDB Rust SDK Reference

Imports

use spacetimedb::{
    procedure, reducer, table, Filter, Identity, ProcedureContext, Query,
    ReducerContext, SpacetimeType, Table, ConnectionId, ScheduleAt,
    TimeDuration, Timestamp, Uuid,
};

**`Table` is required.** Without it, `ctx.db.*.insert()`, `.iter()`, `.find()` etc. won't compile (`no method named 'insert' found`).

Tables

`#[spacetimedb::table(...)]` on a `pub struct`. `accessor` must be snake_case:

#[spacetimedb::table(accessor = entity, public)]
pub struct Entity {
    #[primary_key]
    #[auto_inc]
    pub id: u64,
    pub owner: Identity,
    pub name: String,
    #[index(btree)]
    pub active: bool,
}

Options: `accessor = snake_case` (required), `public`, `scheduled(reducer_fn)`, `index(...)`

`ctx.db` accessors use the `accessor` name (snake_case).

Column Types

| Rust type | Notes | |-----------|-------| | `u8` / `u16` / `u32` / `u64` / `u128` | unsigned integers | | `i8` / `i16` / `i32` / `i64` / `i128` | signed integers | | `spacetimedb::sats::u256` / `spacetimedb::sats::i256` | 256-bit integers | | `f32` / `f64` | floats | | `bool` | boolean | | `String` | text | | `Vec<T>` | list/array | | `Identity` | user identity | | `ConnectionId` | connection handle | | `Timestamp` | server timestamp (microseconds since epoch) | | `TimeDuration` | duration in microseconds | | `Uuid` | UUID | | `Option<T>` | nullable column |

Column Attributes

#[primary_key]          // primary key
#[auto_inc]             // auto-increment (use 0 as placeholder on insert)
#[unique]               // unique constraint
#[index(btree)]         // btree index (enables .filter() on this column)
#[default(true)]        // migration-safe default for a newly appended column

Defaults support compatible addition of a newly appended field. Do not place `#[default(...)]` on primary-key, unique, or auto-increment columns.

Indexes

Prefer `#[index(btree)]` inline for single-column. Multi-column uses table-level:

// Inline (preferred for single-column):
#[index(btree)]
pub author_id: u64,
// Access: ctx.db.post().author_id().filter(author_id)

// Multi-column (table-level):
#[spacetimedb::table(accessor = membership, public,
    index(accessor = by_group_user, btree(columns = [group_id, user_id]))
)]
pub struct Membership { pub group_id: u64, pub user_id: Identity, ... }
// Access: ctx.db.membership().by_group_user().filter((group_id, &user_id))

When you frequently look up rows by multiple columns, prefer a multi-column index over filtering by one column and looping over the results.

Reducers

#[spacetimedb::reducer]
pub fn create_entity(ctx: &ReducerContext, name: String) {
    ctx.db.entity().insert(Entity { id: 0, owner: ctx.sender(), name, active: true });
}

// Reducers can return Result<(), String> or Result<(), E> where E: Display
#[spacetimedb::reducer]
pub fn validate_entity(ctx: &ReducerContext, name: String) -> Result<(), String> {
    if name.is_empty() {
        return Err("Name cannot be empty".to_string());
    }
    ctx.db.entity().try_insert(Entity { id: 0, owner: ctx.sender(), name, active: true })?;
    Ok(())
}

Note: `insert()` panics on constraint violations. Use `try_insert()` with `?` when returning `Result`.

DB Operations

ctx.db.entity().insert(Entity { id: 0, name: "Sample".into() });  // Insert (0 for autoInc)
ctx.db.entity().id().find(entity_id);                              // Find by PK → Option<Entity>
ctx.db.entity().identity().find(ctx.sender());                     // Find by unique column → Option<Entity>
ctx.db.item().author_id().filter(author_id);                       // Filter by index → iterator
ctx.db.entity().iter();                                            // All rows → iterator
ctx.db.entity().count();                                           // Count rows
ctx.db.entity().id().update(Entity { name: new_name, ..existing }); // Update (override + spread)
ctx.db.entity().id().delete(entity_id);                            // Delete by PK
ctx.db.entity().name().delete("Alice".to_string());                // Delete by indexed String column

Note: `iter()` and `filter()` return iterators. Collect to Vec if you need `.sort()`, `.filter()`, `.map()`.

Range queries on btree indexes: `filter(18..=65)`, `filter(18..)`, `filter(..18)`.

String column accessors operate on the column's owned `String` type, not `&str`. Pass a `String` or `&String` to `find` and `delete`; index filters borrow the key, as in `ctx.db.product().category().filter(&"hardware".to_string())`.

Lifecycle Hooks

#[spacetimedb::reducer(init)]
pub fn init(ctx: &ReducerContext) { ... }

#[spacetimedb::reducer(client_connected)]
pub fn on_connect(ctx: &ReducerContext) { ... }

#[spacetimedb::reducer(client_disconnected)]
pub fn on_disconnect(ctx: &ReducerContext) { ... }

The current connection ID is available through `ctx.connection_id()` (not a public field) and may be absent outside connection-scoped calls.

Views

// Anonymous view (same result for all clients):
use spacetimedb::{view, AnonymousViewContext};

#[view(accessor = active_users, public)]
fn active_users(ctx: &AnonymousViewContext) -> Vec<Entity> {
    ctx.db.entity().active().filter(true).collect()
}

// Per-user view (result varies by sender):
use spacetimedb::{view, ViewContext};

#[view(accessor = my_profile, public)]
fn my_profile(ctx: &ViewContext) -> Option<Entity> {
    ctx.db.entity().identity().find(ctx.sender())
}

Procedural-view table handles support indexed `find` and `filter` access, but not full-table `iter()`. Start a procedural view from an approp

Read more
@clockworklabs
Ships withspacetimedb

Development at the speed of light

26k11 skills1 MCP
Get the whole plugin

Stars on GitHub

Momentum

stars in total
25.5k
Star history of clockworklabs/spacetimedb on GitHub, a chart by star-history.com

Other skills on spacetimedb.

cli

cli

SpacetimeDB CLI reference for initializing projects, building modules, publishing databases, querying data, and managing servers

concepts

concepts

Understand SpacetimeDB architecture and core concepts. Use when learning SpacetimeDB or making architectural decisions.

csharp-client

csharp-client

SpacetimeDB C#/.NET client SDK reference. Use when building C# clients that connect to SpacetimeDB (console, desktop, or any .NET…

mcp

mcp

Operate a running SpacetimeDB database through MCP tools rather than the CLI - list databases, read schemas, run SQL, and call…

More skills from other plugins.

backend

backend

Use this skill when contributing to InsForge's backend package. This is for maintainers editing backend routes, services,…

@insforge@insforgeInstall Skill
exa-agent

exa-agent

Use Exa Agent for multi-step web research, list-building, enrichment, structured output, run continuation, and coverage…

exa12d5.2k
@exa-labs@exa-labsInstall Skill
fde-ontology

fde-ontology

多源输入→本体检出→AI审核→场景升级→代码生成 自动化管线

dgiot4w4.9k
@dgiot@dgiotInstall Skill

Questions about rust-server.

Which coding agents can use rust-server?
rust-server installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.