Table Access Permissions
SpacetimeDB enforces different levels of table access depending on the context. All contexts access tables through ctx.db, but the available operations differ based on whether the context is read-write or read-only.
Reducers - Read-Write Access
Reducers receive a ReducerContext which provides full read-write access to tables. They can perform all CRUD operations: insert, read, update, and delete.
- TypeScript
- C#
- Rust
spacetimedb.reducer('example', {}, (ctx) => {
// Insert
ctx.db.user.insert({ id: 0, name: 'Alice', email: 'alice@example.com' });
// Read: iterate all rows
for (const user of ctx.db.user.iter()) {
console.log(user.name);
}
// Read: find by unique column
const foundUser = ctx.db.user.id.find(123);
if (foundUser) {
// Update
foundUser.name = 'Bob';
ctx.db.user.id.update(foundUser);
}
// Delete
ctx.db.user.id.delete(456);
});[SpacetimeDB.Reducer]
public static void Example(ReducerContext ctx)
{
// Insert
ctx.Db.User.Insert(new User { Id = 0, Name = "Alice", Email = "alice@example.com" });
// Read: iterate all rows
foreach (var user in ctx.Db.User.Iter())
{
Log.Info($"User: {user.Name}");
}
// Read: find by unique column
if (ctx.Db.User.Id.Find(123) is User foundUser)
{
// Update
foundUser.Name = "Bob";
ctx.Db.User.Id.Update(foundUser);
}
// Delete
ctx.Db.User.Id.Delete(456);
}#[spacetimedb::reducer]
fn example(ctx: &ReducerContext) -> Result<(), String> {
// Insert
ctx.db.user().insert(User {
id: 0,
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
});
// Read: iterate all rows
for user in ctx.db.user().iter() {
log::info!("User: {}", user.name);
}
// Read: find by unique column
if let Some(mut user) = ctx.db.user().id().find(123) {
// Update
user.name = "Bob".to_string();
ctx.db.user().id().update(user);
}
// Delete
ctx.db.user().id().delete(456);
Ok(())
}Procedures with Transactions - Read-Write Access
Procedures receive a ProcedureContext and can access tables through transactions. Unlike reducers, procedures must explicitly open a transaction to read from or modify the database.
- TypeScript
- C#
- Rust
spacetimedb.procedure('updateUserProcedure', { userId: t.u64(), newName: t.string() }, t.unit(), (ctx, { userId, newName }) => {
// Must explicitly open a transaction
ctx.withTx(ctx => {
// Full read-write access within the transaction
const user = ctx.db.user.id.find(userId);
if (user) {
user.name = newName;
ctx.db.user.id.update(user);
}
});
// Transaction is committed when the function returns
return {};
});
Support for procedures in C# modules is coming soon!
#[spacetimedb::procedure]
fn update_user_procedure(ctx: &mut ProcedureContext, user_id: u64, new_name: String) {
// Must explicitly open a transaction
ctx.with_tx(|ctx| {
// Full read-write access within the transaction
if let Some(mut user) = ctx.db.user().id().find(user_id) {
user.name = new_name.clone();
ctx.db.user().id().update(user);
}
});
// Transaction is committed when the closure returns
}See the Procedures documentation for more details on using procedures, including making HTTP requests to external services.
Views - Read-Only Access
Views receive a ViewContext or AnonymousViewContext which provides read-only access to tables. They can query and iterate tables, but cannot insert, update, or delete rows.
- TypeScript
- C#
- Rust
spacetimedb.view(
{ name: 'findUsersByName', public: true },
t.array(user.rowType),
(ctx) => {
// Can read and filter
return Array.from(ctx.db.user.name.filter('Alice'));
// Cannot insert, update, or delete
// ctx.db.user.insert(...) // ❌ Method not available
});[SpacetimeDB.View(Name = "FindUsersByName", Public = true)]
public static List<User> FindUsersByName(ViewContext ctx)
{
// Can read and filter
return ctx.Db.User.Name.Filter("Alice").ToList();
// Cannot insert, update, or delete
// ctx.Db.user.Insert(...) // ❌ Method not available
}#[spacetimedb::view(name = find_users_by_name, public)]
fn find_users_by_name(ctx: &ViewContext) -> Vec<User> {
// Can read and filter
ctx.db.user().name().filter("Alice").collect()
// Cannot insert, update, or delete
// ctx.db.user().insert(...) // ❌ Compile error
}Client Access - Read-Only Access
Clients connect to databases and can access public tables through subscriptions and queries. See the Subscriptions documentation for details on client-side table access.