douglance/spacetimedb/spacetimedb-typescript

Claude Code and agent skill by douglance/spacetimedb

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Build TypeScript clients for SpacetimeDB. Use when connecting to SpacetimeDB from web apps, Node.js, Deno, Bun, or other JavaScript runtimes.

Install

npx vskill@latest install douglance/spacetimedb/spacetimedb-typescript

SKILL.md

SpacetimeDB TypeScript SDK

Build real-time TypeScript clients that connect directly to SpacetimeDB modules. The SDK provides type-safe database access, automatic synchronization, and reactive updates for web apps, Node.js, Deno, Bun, and other JavaScript runtimes.


HALLUCINATED APIs — DO NOT USE

These APIs DO NOT EXIST. LLMs frequently hallucinate them.

typescript
// WRONG PACKAGE — does not exist
import { SpacetimeDBClient } from "@clockworklabs/spacetimedb-sdk";
// WRONG — these methods don't exist
SpacetimeDBClient.connect(...);
SpacetimeDBClient.call("reducer_name", [...]);
connection.call("reducer_name", [arg1, arg2]);
// WRONG — positional reducer arguments
conn.reducers.doSomething("value"); // WRONG!
CORRECT PATTERNS:
typescript
// CORRECT IMPORTS
import { DbConnection, tables } from './module_bindings'; // Generated!
import { SpacetimeDBProvider, useTable, Identity } from 'spacetimedb/react';
// CORRECT REDUCER CALLS — object syntax, not positional!
conn.reducers.doSomething({ value: 'test' });
conn.reducers.updateItem({ itemId: 1n, newValue: 42 });
// CORRECT DATA ACCESS — useTable returns [rows, isLoading]
const [items, isLoading] = useTable(tables.item);
DO NOT:
  • Invent hooks like useItems(), useData() — use useTable(tables.tableName)
  • Import from fake packages — only spacetimedb, spacetimedb/react, ./module_bindings

Common Mistakes Table

Server-side errors
WrongRightError
Missing package.jsonCreate package.json"could not detect language"
Missing tsconfig.jsonCreate tsconfig.json"TsconfigNotFound"
Entrypoint not at src/index.tsUse src/index.tsModule won't bundle
indexes in COLUMNS (2nd arg)indexes in OPTIONS (1st arg)"reading 'tag'" error
Index without algorithmalgorithm: 'btree'"reading 'tag'" error
filter({ ownerId })filter(ownerId)"does not exist in type 'Range'"
.filter() on unique column.find() on unique columnTypeError
insert({ ...without id })insert({ id: 0n, ... })"Property 'id' is missing"
const id = table.insert(...)const row = table.insert(...).insert() returns ROW, not ID
.unique() + explicit indexJust use .unique()"name is used for multiple entities"
Import spacetimedb from index.tsImport from schema.ts"Cannot access before initialization"
Multi-column index .filter()Use single-column indexPANIC or silent empty results
.iter() in viewsUse index lookups onlyViews can't scan tables
ctx.db in proceduresctx.withTx(tx => tx.db...)Procedures need explicit transactions
ctx.myTable in procedure txtx.db.myTableWrong context variable
Client-side errors
WrongRightError
@spacetimedb/sdkspacetimedb404 / missing subpath
conn.reducers.foo("val")conn.reducers.foo({ param: "val" })Wrong reducer syntax
Inline connectionBuilderuseMemo(() => ..., [])Reconnects every render
const rows = useTable(table)const [rows, isLoading] = useTable(table)Tuple destructuring
Optimistic UI updatesLet subscriptions drive stateDesync issues
<SpacetimeDBProvider builder={...}>connectionBuilder={...}Wrong prop name

Hard Requirements

  1. DO NOT edit generated bindings — regenerate with spacetime generate
  2. Reducers are transactional — they do not return data
  3. Reducers must be deterministic — no filesystem, network, timers, random
  4. Reducer calls use object syntax — { param: 'value' } not positional args
  5. Import `DbConnection` from `./module_bindings` — not from spacetimedb
  6. useTable returns a tuple — const [rows, isLoading] = useTable(tables.myTable)
  7. Memoize connectionBuilder — wrap in useMemo(() => ..., []) to prevent reconnects
  8. Views can only use index lookups — .iter() is not allowed in views

Installation

bash
npm install spacetimedb
# or
pnpm add spacetimedb
# or
yarn add spacetimedb

For Node.js 18-21, install the undici peer dependency:

bash
npm install spacetimedb undici

Node.js 22+ and browser environments work out of the box.

Generating Type Bindings

Before using the SDK, generate TypeScript bindings from your SpacetimeDB module:

bash
spacetime generate --lang typescript --out-dir ./src/module_bindings --project-path ./server

This creates a module_bindings directory with:

  • index.ts - Main exports including DbConnection, tables, reducers, query
  • Type definitions for each table (e.g., player_table.ts, user_table.ts)
  • Type definitions for each reducer (e.g., create_player_reducer.ts)
  • Custom type definitions (e.g., point_type.ts)

Basic Connection Setup

typescript
import { DbConnection } from './module_bindings';
const connection = DbConnection.builder()
.withUri('ws://localhost:3000')
.withModuleName('my_database')
.onConnect((conn, identity, token) => {
console.log('Connected with identity:', identity.toHexString());
// Store token for reconnection
localStorage.setItem('spacetimedb_token', token);
// Subscribe to tables after connection
conn.subscriptionBuilder().subscribe('SELECT * FROM player');
})
.onDisconnect((ctx) => {
console.log('Disconnected');
})
.onConnectError((ctx, error) => {
console.error('Connection error:', error);
})
.build();

Connection Builder Options

typescript
DbConnection.builder()
// Required: SpacetimeDB server URI
.withUri('ws://localhost:3000')
// Required: Database module name or address
.withModuleName('my_database')
// Optional: Authentication token for reconnection
.withToken(localStorage.getItem('spacetimedb_token') ?? undefined)
// Optional: Enable compression (default: 'gzip')
.withCompression('gzip') // or 'none'
// Optional: Light mode reduces network traffic
.withLightMode(true)
// Optional: Wait for durable writes before receiving updates
.withConfirmedReads(true)
// Connection lifecycle callbacks
.onConnect((conn, identity, token) => { /* ... */ })
.onDisconnect((ctx, error) => { /* ... */ })
.onConnectError((ctx, error) => { /* ... */ })
.build();

Subscribing to Tables

Subscriptions sync table data to the client cache. Use SQL queries to filter what data you receive.

Basic Subscription
typescript
connection.subscriptionBuilder()
.onApplied((ctx) => {
console.log('Subscription applied, cache is ready');
})
.onError((ctx, error) => {
console.error('Subscription error:', error);
})
.subscribe('SELECT * FROM player');
Multiple Queries
typescript
connection.subscriptionBuilder()
.subscribe([
'SELECT * FROM player',
'SELECT * FROM game_state',
'SELECT * FROM message WHERE room_id = 1'
]);
Typed Query Builder

Use the generated query object for type-safe queries:

typescript
import { query } from './module_bindings';
// Simple query - selects all rows
connection.subscriptionBuilder()
.subscribe(query.player.build());
// Query with WHERE clause
connection.subscriptionBuilder()
.subscribe(
query.player
.where(row => row.name.eq('Alice'))
.build()
);
// Complex conditions
connection.subscriptionBuilder()
.subscribe(
query.player
.where(row => row.score.gte(100))
.where(row => row.isActive.eq(true))
.build()
);
Subscribe to All Tables

For development or small datasets:

typescript
connection.subscriptionBuilder().subscribeToAllTables();
Unsubscribing
typescript
const handle = connection.subscriptionBuilder()
.onApplied(() => console.log('Subscribed'))
.subscribe('SELECT * FROM player');
// Later, unsubscribe
handle.unsubscribe();
// Or with callback when complete
handle.unsubscribeThen((ctx) => {
console.log('Unsubscribed successfully');
});

Accessing Table Data

After subscription, access cached data through connection.db:

typescript
// Iterate all rows
for (const player of connection.db.player.iter()) {
console.log(player.name, player.score);
}
// Convert to array
const players = Array.from(connection.db.player.iter());
// Count rows
const count = connection.db.player.count();
// Find by primary key (if table has one)
const player = connection.db.player.id.find(42);
// Find by indexed column
const alice = connection.db.player.name.find('Alice');

Table Event Callbacks

Listen for real-time changes to table data:

typescript
// Row inserted
connection.db.player.onInsert((ctx, player) => {
console.log('New player:', player.name);
});
// Row deleted
connection.db.player.onDelete((ctx, player) => {
console.log('Player left:', player.name);
});
// Row updated (requires primary key on table)
connection.db.player.onUpdate((ctx, oldPlayer, newPlayer) => {
console.log(`${oldPlayer.name} score: ${oldPlayer.score} -> ${newPlayer.score}`);
});
// Remove callbacks
const onInsertCb = (ctx, player) => console.log(player);
connection.db.player.onInsert(onInsertCb);
connection.db.player.removeOnInsert(onInsertCb);
Event Context

Callbacks receive an EventContext with information about the event:

typescript
connection.db.player.onInsert((ctx, player) => {
// Access to database
const allPlayers = Array.from(ctx.db.player.iter());
// Check event type
if (ctx.event.tag === 'Reducer') {
const { callerIdentity, reducer, status } = ctx.event.value;
console.log(`Triggered by reducer: ${reducer.name}`);
}
// Call other reducers
ctx.reducers.sendMessage({ playerId: player.id, text: 'Welcome!' });
});

Calling Reducers

Reducers are server-side functions that modify the database. CRITICAL: Use object syntax, not positional arguments.

typescript
// CORRECT: Object syntax
connection.reducers.createPlayer({ name: 'Alice', location: { x: 0, y: 0 } });
// WRONG: Positional arguments
// connection.reducers.createPlayer('Alice', { x: 0, y: 0 }); // DO NOT DO THIS
// Listen for reducer results
connection.reducers.onCreatePlayer((ctx, args) => {
const { callerIdentity, status, timestamp, energyConsumed } = ctx.event;
if (status.tag === 'Committed') {
console.log('Player created successfully');
} else if (status.tag === 'Failed') {
console.error('Failed:', status.value);
}
});
// Remove reducer callback
connection.reducers.removeOnCreatePlayer(callback);
Snake_case to camelCase conversion
  • Server: spacetimedb.reducer('do_something', ...)
  • Client: conn.reducers.doSomething({ ... })
Reducer Flags

Control how the server handles reducer calls:

typescript
// NoSuccessNotify: Don't send TransactionUpdate on success (reduces traffic)
connection.setReducerFlags.movePlayer('NoSuccessNotify');
// FullUpdate: Always send full TransactionUpdate (default)
connection.setReducerFlags.movePlayer('FullUpdate');

Views

Views provide filtered access to private table data based on the connected user.

ViewContext vs AnonymousViewContext
typescript
// ViewContext — has ctx.sender, result varies per user (computed per-subscriber)
spacetimedb.view({ name: 'my_items', public: true }, t.array(Item.rowType), (ctx) => {
return [...ctx.db.item.by_owner.filter(ctx.sender)];
});
// AnonymousViewContext — no ctx.sender, same result for everyone (shared, better perf)
spacetimedb.anonymousView({ name: 'leaderboard', public: true }, t.array(LeaderboardRow), (ctx) => {
return [...ctx.db.player.by_score.filter(/* top scores */)];
});
CRITICAL: Views can only use index lookups
typescript
// WRONG — views cannot use .iter()
spacetimedb.view(
{ name: 'my_data_wrong', public: true },
t.array(PrivateData.rowType),
(ctx) => [...ctx.db.privateData.iter()] // NOT ALLOWED
);

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 95 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

7-day trend score
0

Refreshed 1 Apr 2026, 7 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/douglance/spacetimedb/spacetimedb-typescript/badge)](https://verified-skill.com/skills/douglance/spacetimedb/spacetimedb-typescript)

Something wrong with this skill or its page? Tell us.