Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,31 @@ use spacetimedb::Table;
If you see errors like "no method named `try_insert` found", add this import.
:::

</TabItem>
<TabItem value="cpp" label="C++">

Use the `SPACETIMEDB_REDUCER` macro on a function:

```cpp
#include <spacetimedb.h>
using namespace SpacetimeDB;

SPACETIMEDB_REDUCER(create_user, ReducerContext ctx, std::string name, std::string email) {
// Validate input
if (name.empty()) {
return Err("Name cannot be empty");
}

// Modify tables
User user{0, name, email}; // 0 for id - auto-increment will assign
ctx.db[user].insert(user);

return Ok();
}
```

Reducers must take `ReducerContext ctx` as their first parameter. Additional parameters can be any registered types. Reducers return `ReducerResult` (which is `Outcome<void>`): use `Ok()` on success or `Err(message)` on error for convenience.

</TabItem>
</Tabs>

Expand Down Expand Up @@ -161,6 +186,17 @@ ctx.db.user().insert(User {
});
```

</TabItem>
<TabItem value="cpp" label="C++">

```cpp
ctx.db[user].insert(User{
0, // auto-increment will assign
"Alice",
"alice@example.com"
});
```

</TabItem>
</Tabs>

Expand Down Expand Up @@ -204,6 +240,17 @@ if let Some(user) = ctx.db.user().id().find(123) {
let by_email = ctx.db.user().email().find("alice@example.com");
```

</TabItem>
<TabItem value="cpp" label="C++">

```cpp
if (auto user = ctx.db[user_id].find(123)) {
LOG_INFO("Found: " + user->name);
}

auto by_email = ctx.db[user_email].find("alice@example.com");
```

</TabItem>
</Tabs>

Expand Down Expand Up @@ -239,6 +286,15 @@ for user in ctx.db.user().name().filter("Alice") {
}
```

</TabItem>
<TabItem value="cpp" label="C++">

```cpp
for (const auto& user : ctx.db[user_name].filter("Alice")) {
LOG_INFO("User " + std::to_string(user.id) + ": " + user.email);
}
```

</TabItem>
</Tabs>

Expand Down Expand Up @@ -279,6 +335,16 @@ if let Some(mut user) = ctx.db.user().id().find(123) {
}
```

</TabItem>
<TabItem value="cpp" label="C++">

```cpp
if (auto user = ctx.db[user_id].find(123)) {
user->name = "Bob";
ctx.db[user_id].update(*user);
}
```

</TabItem>
</Tabs>

Expand Down Expand Up @@ -322,6 +388,22 @@ let deleted = ctx.db.user().name().delete("Alice");
log::info!("Deleted {} row(s)", deleted);
```

</TabItem>
<TabItem value="cpp" label="C++">

```cpp
// Delete by primary key
ctx.db[user_id].delete_by_key(123);

// Delete all matching an indexed column
uint32_t deleted = 0;
for (const auto& user : ctx.db[user_name].filter("Alice")) {
ctx.db[user_id].delete_by_key(user.id);
deleted++;
}
LOG_INFO("Deleted " + std::to_string(deleted) + " row(s)");
```

</TabItem>
</Tabs>

Expand Down Expand Up @@ -357,6 +439,15 @@ for user in ctx.db.user().iter() {
}
```

</TabItem>
<TabItem value="cpp" label="C++">

```cpp
for (const auto& user : ctx.db[user]) {
LOG_INFO(std::to_string(user.id) + ": " + user.name);
}
```

</TabItem>
</Tabs>

Expand Down Expand Up @@ -388,6 +479,14 @@ let total = ctx.db.user().count();
log::info!("Total users: {}", total);
```

</TabItem>
<TabItem value="cpp" label="C++">

```cpp
auto total = ctx.db[user].count();
LOG_INFO("Total users: " + std::to_string(total));
```

</TabItem>
</Tabs>

Expand Down Expand Up @@ -553,6 +652,48 @@ fn queue_fetch(ctx: &ReducerContext, url: String) {
}
```

</TabItem>
<TabItem value="cpp" label="C++">

```cpp
#define SPACETIMEDB_UNSTABLE_FEATURES
#include <spacetimedb.h>
using namespace SpacetimeDB;

// Define a table to store scheduled tasks
struct FetchSchedule {
uint64_t scheduled_id;
ScheduleAt scheduled_at;
std::string url;
};
SPACETIMEDB_STRUCT(FetchSchedule, scheduled_id, scheduled_at, url);
SPACETIMEDB_TABLE(FetchSchedule, fetch_schedule, Private);
FIELD_PrimaryKeyAutoInc(fetch_schedule, scheduled_id);

// Register the table for scheduling (column 1 = scheduled_at field, 0-based index)
SPACETIMEDB_SCHEDULE(fetch_schedule, 1, fetch_external_data);

// The procedure to be scheduled - called automatically when the time arrives
SPACETIMEDB_PROCEDURE(uint32_t, fetch_external_data, ProcedureContext ctx, FetchSchedule schedule) {
LOG_INFO("Fetching data from: " + schedule.url);
// Process response...
return 0; // Success
}

// From a reducer, schedule the procedure by inserting into the schedule table
SPACETIMEDB_REDUCER(queue_fetch, ReducerContext ctx, std::string url) {
auto scheduled_at = ScheduleAt(TimeDuration::from_seconds(0)); // Run immediately
FetchSchedule fetch_task{
0, // scheduled_id - auto-increment will assign
scheduled_at, // When to execute
url
};
ctx.db[fetch_schedule].insert(fetch_task);
LOG_INFO("Fetch scheduled for URL: " + url);
return Ok();
}
```

</TabItem>
</Tabs>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,27 @@ fn create_user(ctx: &ReducerContext, name: String) {
}
```

</TabItem>
<TabItem value="cpp" label="C++">

```cpp
#include <spacetimedb.h>
using namespace SpacetimeDB;

struct User {
uint64_t id;
std::string name;
};
SPACETIMEDB_STRUCT(User, id, name);
SPACETIMEDB_TABLE(User, user, Public);
FIELD_PrimaryKeyAutoInc(user, id);

SPACETIMEDB_REDUCER(create_user, ReducerContext ctx, std::string name) {
ctx.db[user].insert(User{0, name});
return Ok();
}
```

</TabItem>
</Tabs>

Expand Down Expand Up @@ -184,6 +205,35 @@ fn update_score(ctx: &ReducerContext, new_score: u32) {
}
```

</TabItem>
<TabItem value="cpp" label="C++">

```cpp
#include <spacetimedb.h>
using namespace SpacetimeDB;

struct Player {
Identity identity;
std::string name;
uint32_t score;
};
SPACETIMEDB_STRUCT(Player, identity, name, score);
SPACETIMEDB_TABLE(Player, player, Public);
FIELD_PrimaryKey(player, identity);

SPACETIMEDB_REDUCER(update_score, ReducerContext ctx, uint32_t new_score) {
// Get the caller's identity
auto caller = ctx.sender;

// Find and update their player record
if (auto player = ctx.db[player_identity].find(caller)) {
player->score = new_score;
ctx.db[player_identity].update(*player);
}
return Ok();
}
```

</TabItem>
</Tabs>

Expand Down Expand Up @@ -298,6 +348,36 @@ fn send_reminder(ctx: &ReducerContext, task: ScheduledTask) {
}
```

</TabItem>
<TabItem value="cpp" label="C++">

```cpp
#include <spacetimedb.h>
using namespace SpacetimeDB;

struct ScheduledTask {
uint64_t task_id;
ScheduleAt scheduled_at;
std::string message;
};
SPACETIMEDB_STRUCT(ScheduledTask, task_id, scheduled_at, message);
SPACETIMEDB_TABLE(ScheduledTask, scheduled_task, Private);
FIELD_PrimaryKeyAutoInc(scheduled_task, task_id);

// Register the table for scheduling (column 1 = scheduled_at field, 0-based index)
SPACETIMEDB_SCHEDULE(scheduled_task, 1, send_reminder);

SPACETIMEDB_REDUCER(send_reminder, ReducerContext ctx, ScheduledTask task) {
// Only allow the scheduler (module identity) to call this
if (ctx.sender != ctx.identity()) {
return Err("This reducer can only be called by the scheduler");
}

LOG_INFO("Reminder: " + task.message);
return Ok();
}
```

</TabItem>
</Tabs>

Expand Down Expand Up @@ -346,4 +426,23 @@ TypeScript uses `Math.random()` for random number generation, which is automatic
- `random<T>() -> T` - Generate a single random value
- `sender_auth() -> &AuthCtx` - Get authorization context for the caller (includes JWT claims and internal call detection)
</TabItem>
<TabItem value="cpp" label="C++">

| Property | Type | Description |
| --------------- | ------------------------------ | ----------------------------------------------- |
| `db[table]` | `Table<T>` | Access to a specific table's operations |
| `sender` | `Identity` | Identity of the caller |
| `timestamp` | `Timestamp` | Time when the reducer was invoked |
| `connection_id` | `std::optional<ConnectionId>` | Connection ID of the caller, if available |

**Methods:**

- `identity() -> Identity` - Get the module's identity
- `rng() -> StdbRng&` - Get the random number generator (deterministic and reproducible)
- `sender_auth() -> const AuthCtx&` - Get authorization context for the caller (includes JWT claims and internal call detection)

:::note
C++ uses the `std::optional` type for the `connection_id` to represent values that may not be present. The `rng()` method returns a deterministic random number generator that is seeded consistently across all nodes.
:::
</TabItem>
</Tabs>
Loading
Loading