Skip to content

mitsh/spacetimedb-dart

Repository files navigation

spacetimedb

pub package License: Apache-2.0

Dart SDK for SpacetimeDB v2. Real-time sync, BSATN codec, code generation, and offline-first support.

spacetimedb provides the runtime client, BSATN codec, and generator tooling needed to build type-safe Flutter/Dart apps on top of SpacetimeDB v2. It supports live table updates via the modern SubscribeMulti protocol, server-defined views, reducer calls, authentication, and offline mutation replay.

Compatibility

SDK Version SpacetimeDB Server Protocol
1.3.0+ v2.0+ SubscribeMulti (modern)
0.1.0 v1.12.x Subscribe (legacy)

Features

  • SpacetimeDB v2 support — SubscribeMulti protocol, server-defined views, updated message format
  • WebSocket connection with reconnect handling and TLS support
  • BSATN binary encoding/decoding for SpacetimeDB data types
  • Generated, type-safe APIs for tables, reducers, enums, and views
  • Real-time table cache with synchronous insert/update/delete streams
  • Subscription API for SQL-based live queries (multi-query batching)
  • Authentication utilities with token persistence support
  • Offline-first mutation queue with optimistic updates
  • Web-safe storage with conditional imports (no dart:io dependency on web)

Installation

dependencies:
  spacetimedb: ^1.3.0

Then install dependencies:

dart pub get

Quick Start

Generate a typed client from your SpacetimeDB module:

dart run spacetimedb:generate -s http://localhost:3000 -d your_database -o lib/generated

Use the generated client in your app:

import 'package:spacetimedb/spacetimedb.dart';
import 'generated/client.dart';

final client = await SpacetimeDbClient.connect(
  host: 'localhost:3000',
  database: 'your_database',
  ssl: false,
  authStorage: InMemoryTokenStore(),
  initialSubscriptions: ['SELECT * FROM users'],
);

Usage

Connection

final client = await SpacetimeDbClient.connect(
  host: 'localhost:3000',
  database: 'your_database',
  ssl: false,
  authStorage: InMemoryTokenStore(),
);

client.connection.connectionStatus.listen((status) {
  print('Connection status: $status');
});

Tables

for (final user in client.users.iter()) {
  print('User: ${user.name}');
}

client.users.insertStream.listen((user) {
  print('Inserted user: ${user.name}');
});

Views (SpacetimeDB v2)

Views are server-defined, identity-scoped projections. The code generator produces typed view accessors alongside tables:

// Views work like tables — same cache and stream APIs
for (final game in client.myGames.iter()) {
  print('Game: ${game.id}, status: ${game.status}');
}

// Subscribe to view changes
client.myPlayerData.updateStream.listen((update) {
  print('Energy changed: ${update.old.energy} → ${update.new_.energy}');
});

Reducers

final result = await client.reducers.createUser(name: 'Alice');
if (result.isSuccess) {
  print('Reducer call succeeded');
}

Subscriptions (SubscribeMulti)

The SDK uses the modern SubscribeMulti protocol for efficient multi-query subscription batching:

await client.subscriptions.subscribe([
  'SELECT * FROM users WHERE active = true',
  'SELECT * FROM games WHERE status = "active"',
]);

Authentication

final client = await SpacetimeDbClient.connect(
  host: 'spacetimedb.example.com',
  database: 'app_db',
  ssl: true,
  authStorage: InMemoryTokenStore(),
);

print(client.identity?.toHexString);

Offline Support

final client = await SpacetimeDbClient.connect(
  host: 'localhost:3000',
  database: 'your_database',
  offlineStorage: JsonFileStorage(basePath: '/tmp/spacetimedb_cache'),
);

print('Pending mutations: ${client.syncState.pendingCount}');

Code Generation

Use the bundled executable to generate strongly-typed Dart APIs:

dart run spacetimedb:generate -s http://localhost:3000 -d your_database -o lib/generated

The generator produces typed code for:

  • Tables — Typed row classes with primary key access, iteration, and change streams
  • Views — Same API as tables, for server-defined filtered projections
  • Reducers — Typed caller methods with Future-based results
  • Sum types — Dart enums and sealed classes for SpacetimeDB enums

You can also generate from a local module path:

dart run spacetimedb:generate -p path/to/spacetimedb-module -o lib/generated

API Overview

API Purpose
SpacetimeDbClient.connect(...) Connect to a SpacetimeDB database and initialize generated APIs
client.<table>.iter() Read cached table rows with typed iteration
client.<table>.insertStream Listen for real-time inserts (synchronous delivery)
client.<table>.updateStream Listen for real-time updates with old/new values
client.<table>.deleteStream Listen for real-time deletes
client.<view>.iter() Read cached view rows (identity-scoped)
client.reducers.<name>(...) Call reducers with typed parameters/results
client.subscriptions.subscribe([...]) Start SubscribeMulti live SQL subscriptions
BsatnEncoder / BsatnDecoder Encode/decode BSATN payloads
AuthTokenStore Plug in custom token persistence
OfflineStorage Persist cached data and mutation queue for offline-first flows

Architecture

┌─────────────────────────────────────────────────┐
│                Flutter / Dart App               │
├─────────────────────────────────────────────────┤
│  Generated Client (tables, views, reducers)     │
├─────────────────────────────────────────────────┤
│  spacetimedb SDK                                │
│  ├── TableCache (sync broadcast streams)        │
│  ├── SubscriptionManager (SubscribeMulti)       │
│  ├── ReducerCaller (with offline queue)         │
│  ├── SpacetimeDbConnection (WebSocket + auth)   │
│  └── BSATN Codec (binary encode/decode)         │
├─────────────────────────────────────────────────┤
│  WebSocket (Protobuf) ←→ SpacetimeDB v2 Server  │
└─────────────────────────────────────────────────┘

Platform Support

Platform Runtime Code Generation Offline (File)
Android Yes Yes Yes
iOS Yes Yes Yes
macOS Yes Yes Yes
Windows Yes Yes Yes
Linux Yes Yes Yes
Web Yes N/A No*

* Web builds use InMemoryOfflineStorage. File-based JsonFileStorage requires dart:io and is not available on web. The SDK automatically provides a web-compatible stub that throws UnsupportedError if you try to use JsonFileStorage on web.

Security Considerations

  • Offline storage is unencrypted. Table snapshots and pending mutations are stored as plaintext JSON. Do not persist sensitive data (passwords, tokens, PII) without app-level encryption.
  • Use ssl: true in production. Without SSL, authentication tokens are sent in plaintext over the network.
  • Web platform auth tokens are passed as URL query parameters (WebSocket API limitation). These tokens are short-lived, but may appear in proxy logs. Always use SSL in production.
  • Token storage is pluggable via AuthTokenStore. For production apps, implement a secure storage backend (e.g., flutter_secure_storage).

Logging

By default, the SDK produces no log output. To enable logging:

// Option 1: Route to dart:developer (visible in DevTools)
SdkLogger.enableDeveloperLog();

// Option 2: Custom callback
SdkLogger.onLog = (level, message) {
  // 'D' = debug, 'I' = info, 'W' = warning, 'E' = error
  print('[$level] $message');
};

License

Apache License 2.0. See LICENSE.


Attribution

This project was originally forked from spacetimedb-dart-sdk by Mikael Wills. The original work provided the foundation for the WebSocket connection, BSATN codec, and initial code generation architecture.

About

No description, website, or topics provided.

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors