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.
- Rust
- C#
- TypeScript
#[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(())
}[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('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);
});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.
- Rust
- C#
- TypeScript
#[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
}Support for procedures in C# modules is coming soon!
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 {};
});
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.
- Rust
- C#
- TypeScript
#[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
}[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: '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
});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.