bevy_stdb
A Bevy integration for SpacetimeDB.
Please enjoy this useless AI generated image based on the README contents of this repo.
Overview
bevy_stdb adapts SpacetimeDB's connection and callback model into Bevy-style resources, systems, plugins, and messages.
Features
- Builder-style setup via
StdbPlugin - Connection resource access through
StdbConnection - Table event bridging into normal Bevy
Messages - Managed subscription intent through
StdbSubscriptions - Optional reconnect support through
StdbReconnectOptions
Example
use *;
use *;
use crate;
pub type StdbConn = ;
pub type StdbSubs = ;
Connection driving
bevy_stdb supports two connection-driving modes:
with_background_driver(...): start SpacetimeDB's background processing for the active connectionwith_frame_driver(...): drive SpacetimeDB from the Bevy schedule each frame
Exactly one driver must be configured. These modes are mutually exclusive, and in most applications you'll want with_background_driver(...).
If WASM support is needed, you can enable the browser feature flag in both this crate and your spacetimedb-sdk crate using a target cfg:
# Enable browser support for wasm builds.
# Replace `*` with the versions you are using.
[]
= { = "*", = ["browser"] }
= { = "*", = ["browser"] }
I recommend checking out the bevy_cli 2d template for a good starter example using WASM + native with nice Bevy features configured.
Native background driving
On native targets, the typical choice is run_threaded:
Browser / wasm background driving (async)
On browser targets, use the generated background task helper instead:
If you target both native and browser, I recommend selecting the background driver with cfg:
Bevy frame-tick driving
Use frame_tick when you want Bevy to drive connection progress from Bevy each frame. Internally, bevy_stdb runs this driver from PreUpdate:
use *;
use *;
use crate;
Table registration
Use the StdbPlugin builder methods to register table bindings during app setup.
Each method eagerly registers the Bevy message channels for the row type you specify and stores a deferred binding callback that runs whenever a connection becomes active.
| Method | Use when |
|---|---|
add_table |
Table has a primary key — emits insert, update, and delete messages |
add_table_without_pk |
Table has no primary key — emits insert and delete messages only |
add_event_table |
Append-only log table — emits insert messages only |
add_view |
Server-computed virtual table — emits insert and delete messages |
.
.
.
.
Table message registration happens eagerly at startup; callback binding is deferred until a connection is active.
Messages
Depending on the table shape, bevy_stdb forwards updates into Bevy messages such as:
InsertMessage<T>DeleteMessage<T>UpdateMessage<T>InsertUpdateMessage<T>
This lets normal Bevy systems react to database changes using message readers. These messages include both the affected row data and the SpacetimeDB event that triggered the change.
use crateReducer;
use *;
use Event;
Subscriptions
StdbSubscriptions stores desired subscription intent separately from the live connection. Subscriptions are keyed by a type you define, so you can refer to them by domain-specific identifiers for dynamic resubscription or unsubscription.
Enable it during plugin setup with with_subscriptions, then queue subscriptions from any Bevy system — typically in response to StdbConnectedMessage. Queued intent is automatically re-applied after a reconnect.
There are two ways to subscribe:
subscribe_sql(key, "SELECT * FROM my_table")— raw SQL stringsubscribe_query(key, |q| q.from.my_table())— generated query builder
ReadStdbSubscriptionAppliedMessage and ReadStdbSubscriptionErrorMessage are emitted for the on_applied and on_error callbacks per subscription.
Reconnects
Reconnect behavior is opt-in. Pass StdbReconnectOptions to StdbPlugin::with_reconnect to enable it.
The reconnect cycle activates when a disconnect message includes an error, or when a connection attempt fails — including a first-time failure. A clean disconnect() call does not trigger a retry. While a connection attempt is in-flight the timer is paused; it re-arms once the attempt resolves. The cycle resets fully on a successful connect so the full attempt budget is available again.
.with_reconnect
When a reconnect succeeds:
- the
StdbConnectionresource is replaced - table callbacks are re-bound
- subscriptions are re-applied
Type Aliases
It is useful to define some type aliases of your own. I suggest making aliases for the connection, subscription, and commands:
pub type StdbConn = ;
pub type StdbSubs = ;
pub type StdbCmds<'w, 's> = ;
Using commands
Use StdbCommands<C, M> to connect or disconnect at runtime, optionally overriding the token, URI, or module name configured on the plugin.
pub type StdbCmds<'w, 's> = ;
// Connect with plugin defaults:
// Connect with a runtime token override:
See StdbConnectOptions for all available overrides (from_token, from_uri, from_database_name, from_target).
Connection-dependent resources
bevy_stdb resources are only available while a connection is active. Guard systems with resource_exists::<StdbConnection<_>>() or accept the connection as an optional parameter. If you need to detect that a connection has been lost before the resource is cleaned up, StdbConnection::is_active() checks whether the underlying send channel is still open:
use *;
use *;
use crate;
pub type StdbConn = ;
Compatibility
| bevy_stdb | bevy | spacetimedb_sdk |
|---|---|---|
| 0.1 - 0.2 | 0.18 | 2.0 |
| 0.3 - 0.8 | 0.18 | 2.1 |
Notes
This crate focuses on table-driven client workflows. Reducer and procedure access still exist through the active StdbConnection, but the primary Bevy-facing event flow is table/message based.
Special thanks to bevy_spacetimedb for the inspiration!