โจ SpacetimeDSL โ The SpacetimeDB Rust Server Module meta-framework
SpacetimeDSL allows you to interact in an ergonomic, more developer-friendly and type-safer way with the data in your SpacetimeDB server.
๐ Table of Contents
See DOCUMENTATION.md for a comprehensive reference with all features, examples, and rules.
Core Unique Features
- ๐ Foreign Keys / Referential Integrity โ Enforce relationships between tables with different strategies on deletion.
- ๐ท๏ธ Wrapper Types โ Type-safe column identifiers that eliminate primitive obsession.
- ๐ฒ Unique Multi-Column Indices โ Enforce uniqueness across multiple columns (because SpacetimeDB has no native support).
- ๐ช Hooks System โ Execute custom logic automatically before and after inserts, updates and deletes.
- ๐จ Ergonomic DSL Methods โ DSL equivalents for all SpacetimeDB operations with cleaner syntax and smart defaults.
- ๐ฏ Singleton Tables โ Single-row tables for global config or state.
- ๐๏ธ Read-Only View Support โ Use DSL methods in SpacetimeDB views.
Enhanced Developer Experience
- ๐๏ธ Method Configuration โ Explicit control over which operations are allowed on your tables.
- ๐จ Rich Error Types โ Detailed error information beyond what SpacetimeDB provides.
- ๐ Deletion Results โ Complete audit trails for delete operations with cascade tracking.
- ๐ Automatic Accessors โ Generated getters, mut-getters and setters with visibility controls.
Additional Information
๐ Examples
- blackholio โ Real-world multiplayer game using SpacetimeDSL with foreign keys, hooks, scheduled tasks, and wrapper types.
- test โ Integration test suite covering all SpacetimeDSL features.
๐ Example 2D Tile-based Game
๐ง Vanilla SpacetimeDB
Let's start with a ordinary SpacetimeDB schema:
// FIXME 1: Need to validate that entities are only created and deleted, not updated. Would be cool if the compiler would enforce this, but for now each developer must keep this in mind. I'll create docs on that and hope that everyone reads them...
// FIXME 6: Need to validate where positions are created and changed that x and y is unique together, so that each tile can only contain one Entity. I hope I won't need more unique multi-column indices in the future... Develop a generic solution for this?!
// FIXME 7: Need to validate that positions are in specific bounds where positions are created and changed so that entities like players can't move outside the playable area.
The Problem: SpacetimeDB is great technology, but has weaknesses that prevent developers from utilizing its full potential โ sometimes you work against the database.
โก SpacetimeDB with SpacetimeDSL
Let's see what happens when adding SpacetimeDSL:
// Added
// Added
const WORLD_BOUNDARY: i128 = 10_000_000_000_000_000_000;
// Added
โจ What's different?
Cleaner Modeling:
- ๐ฏ
Entityis constrained to create/delete only (update = false), so lifecycle intent is explicit in the type-level API. - ๐ Sensitive columns (
id,created_at,entity_id,modified_at) are private in the struct, preventing accidental mutation. - ๐ง Generated getters (without setters / mut-getters for immutable fields) enforce safer usage patterns by default.
Smart Defaults & Automation:
- ๐ค Auto-increment IDs are handled automatically (no manual ID assignment, no non-zero ID mistakes).
- โฐ
created_atis set automatically on create. - ๐
modified_atis set toNoneon create and updated toSome(ctx.timestamp)on update. - ๐งท No-op update guard: if position
x/ydidn't change, validation is skipped.
Data Integrity by Construction:
- ๐ท๏ธ Wrapper types (
#[create_wrapper]+#[use_wrapper(EntityId)]) make cross-table ID misuse much harder. - ๐ Foreign-key validation ensures referenced
Entityexists on create (and update, if theentity_idcolumn would be mutable). - ๐งน Referential cleanup on delete keeps dependent
Positionrows in sync automatically when deleting their correspondingEntity. - ๐ฒ
unique_index(name = x_y)enforces unique multi-column(x, y)positions.
Hooks for Domain Rules:
- ๐ช Before-insert and before-update hooks validate world bounds.
๐ Try SpacetimeDSL for yourself, by adding it to your server module's Cargo.toml:
# https://crates.io/crates/spacetimedsl The SpacetimeDB Rust Server Module meta-framework
= { = "0.20.1" }
๐ Get started by adding #[spacetimedsl::dsl] plus helper attributes
#[create_wrapper],#[use_wrapper],#[foreign_key]and#[referenced_by]
to your structs with #[spacetimedb::table]!
๐ฌ Need help?
- Consult the FAQ
- Join the SpacetimeDSL channel of the SpacetimeDB Discord Server
โ ๏ธ Current Limitations
-
If you encounter that you can't access a method on the
(Anonymous)ViewContexttype because it's private, please follow these instructions: https://github.com/tamaro-skaljic/SpacetimeDSL/issues/90#issuecomment-3573925117 until SpacetimeDB#3754 is resolved and released.
โ FAQ
โ Why must #[primary_key] columns be private?
Currently, they are allowed to be public, until SpacetimeDB#3754 is resolved and released.
- ๐ They should never change after insertion
- DSL generates setters and mut-getters for non-private columns
- Making them public would:
- โ Allow changes after creation via setters
- โ Allow direct struct member access
- โ Bypass wrapped types
๐ Licensing
SpacetimeDSL is dual-licensed under:
- โ๏ธ MIT License
- โ๏ธ Apache License (Version 2.0)
Open Source โค๏ธ
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.