Migrating From Native SDK Shape
The Same Module, Both Ways
Section titled “The Same Module, Both Ways”The native module and the equivalent effect-spacetimedb contract — declare the shape, then implement handlers behind the typed group:
import { schema, table, t } from "spacetimedb/server"
const spacetime = schema({ user: table( { public: true }, { id: t.string().primaryKey(), name: t.string() }, ),})
export const user_upsert = spacetime.reducer( { id: t.string(), name: t.string() }, (ctx, { id, name }) => { ctx.db.user.insert({ id, name }) },)
export default spacetimeimport * as Effect from "effect/Effect"import * as Stdb from "effect-spacetimedb"
// 1. declare the contractconst user = Stdb.table("user", { public: true, columns: { id: Stdb.string().primaryKey(), name: Stdb.string(), },})
const Users = Stdb.StdbGroup.make("Users").add( Stdb.StdbFn.reducer("user_upsert", { params: Stdb.struct({ id: Stdb.string(), name: Stdb.string() }), }),)
const Module = Stdb.StdbModule.make("app").addTables(user).add(Users)const { Db } = Module
// 2. implement handlers behind the typed groupconst UsersLive = Stdb.StdbBuilder.group(Module, "Users", { user_upsert: (args) => Effect.gen(function* () { const db = yield* Db yield* db.user.insert(args) }),})Native Shape To Contract Shape
Section titled “Native Shape To Contract Shape”| Native concept | effect-spacetimedb concept |
|---|---|
| reducer function export | StdbFn.reducer(...) in a callable group |
| procedure function export | StdbFn.procedure(...) in a callable group |
| table builder | Stdb.table(...) bound to a variable |
reducer/procedure onSchedule option | StdbFn.scheduledReducer(...) or scheduledProcedure(...) |
| HTTP handler export | StdbHttpGroup plus StdbBuilder.group(...) |
Schedules are registered on the function, not the table: StdbFn.scheduledReducer(...)
and scheduledProcedure(...) compile to the native onSchedule option, which is
where SpacetimeDB now expects the binding. If you are porting a module that
passes scheduled to table(...), that registration is deprecated upstream —
drop it and declare the scheduled callable instead.
Suggested Order
Section titled “Suggested Order”- Move schemas and table declarations into leaf files.
- Declare groups without changing runtime behavior.
- Assemble a module and destructure the typed accessors.
- Port handlers one group at a time.
- Generate the client and replace ad hoc call sites with projected clients.
Keep the runtime constraints in mind: module handlers must remain synchronous, deterministic, and transactional. Network calls, timers, and secrets belong outside reducers and are passed in as arguments or handled by external services.
Not affiliated with SpacetimeDB (Clockwork Laboratories) or Effect (Effectful Technologies).