Connecting to SpacetimeDB
After generating client bindings for your module, you can establish a connection to your SpacetimeDB database from your client application. The DbConnection type provides a persistent WebSocket connection that enables real-time communication with the server.
Prerequisites
Before connecting, ensure you have:
- Generated client bindings for your module
- A published database running on SpacetimeDB (local or on MainCloud)
- The database's URI and name or identity
Basic Connection
Create a connection using the DbConnection builder pattern:
- TypeScript
- C#
- Rust
- Unreal
import { DbConnection } from './module_bindings';
const conn = DbConnection.builder()
.withUri("https://maincloud.spacetimedb.com")
.withDatabaseName("my_database")
.withAutomaticReconnect()
.build();using SpacetimeDB;
var conn = DbConnection.Builder()
.WithUri("https://maincloud.spacetimedb.com")
.WithDatabaseName("my_database")
.WithAutomaticReconnect()
.Build();use module_bindings::DbConnection;
let conn = DbConnection::builder()
.with_uri("https://maincloud.spacetimedb.com")
.with_database_name("my_database")
.build();#include "ModuleBindings/DbConnection.h"
UDbConnection* Conn = UDbConnection::Builder()
->WithUri(TEXT("https://maincloud.spacetimedb.com"))
->WithDatabaseName(TEXT("my_database"))
->Build();Replace "https://maincloud.spacetimedb.com" with your SpacetimeDB host URI, and "my_database" with your database's name or identity.
Connecting to MainCloud
To connect to a database hosted on MainCloud:
- TypeScript
- C#
- Rust
- Unreal
const conn = DbConnection.builder()
.withUri("https://maincloud.spacetimedb.com")
.withDatabaseName("my_database")
.withAutomaticReconnect()
.build();var conn = DbConnection.Builder()
.WithUri("https://maincloud.spacetimedb.com")
.WithDatabaseName("my_database")
.WithAutomaticReconnect()
.Build();let conn = DbConnection::builder()
.with_uri("https://maincloud.spacetimedb.com")
.with_database_name("my_database")
.build();UDbConnection* Conn = UDbConnection::Builder()
->WithUri(TEXT("https://maincloud.spacetimedb.com"))
->WithDatabaseName(TEXT("my_database"))
->Build();Authentication with Tokens
To authenticate with a token (for example, from SpacetimeAuth), provide it when building the connection:
- TypeScript
- C#
- Rust
- Unreal
const conn = DbConnection.builder()
.withUri("https://maincloud.spacetimedb.com")
.withDatabaseName("my_database")
.withToken("your_auth_token_here")
.withAutomaticReconnect()
.build();var conn = DbConnection.Builder()
.WithUri("https://maincloud.spacetimedb.com")
.WithDatabaseName("my_database")
.WithAutomaticReconnect()
.WithToken("your_auth_token_here")
.Build();let conn = DbConnection::builder()
.with_uri("https://maincloud.spacetimedb.com")
.with_database_name("my_database")
.with_token("your_auth_token_here")
.build();UDbConnection* Conn = UDbConnection::Builder()
->WithUri(TEXT("https://maincloud.spacetimedb.com"))
->WithDatabaseName(TEXT("my_database"))
->WithToken(TEXT("your_auth_token_here"))
->Build();The token is sent to the server during connection and validates your identity. See the SpacetimeAuth documentation for details on obtaining and managing tokens.
Advancing the Connection
In C# (including Unity), you must manually advance the connection to process incoming messages. In Unreal Engine, you must either manually advance the connection or enable automatic ticking. If the connection is not advanced, it will not process messages.
Call FrameTick() in your game loop or update method:
- C#
- Unreal
// In Unity, call this in your Update() method
void Update()
{
conn.FrameTick();
}
// Or in a console application, call this in your main loop
while (running)
{
conn.FrameTick();
// Your application logic...
}// Option 1: call FrameTick() from your Actor's Tick() method
void AMyActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (Conn)
{
Conn->FrameTick();
}
}
// Option 2: enable automatic ticking once after building the connection
Conn = Builder->Build();
Conn->SetAutoTicking(true);Failure to advance the connection means your client will not receive any updates from the server, including subscription data, reducer callbacks, or connection events.
In Rust and TypeScript, the connection processes messages automatically via the browser's event loop or Node.js's event loop. No manual polling is required.
Connection Lifecycle
Connection Callbacks
Register callbacks to observe connection state changes:
- TypeScript
- C#
- Rust
- Unreal
const HOST = "https://maincloud.spacetimedb.com";
const DB_NAME = "my_database";
const TOKEN_KEY = `${HOST}/${DB_NAME}/auth_token`;
const conn = DbConnection.builder()
.withUri(HOST)
.withDatabaseName(DB_NAME)
.withToken(localStorage.getItem(TOKEN_KEY) ?? undefined)
.withAutomaticReconnect()
.onConnect((conn, identity, token) => {
console.log(`Connected! Identity: ${identity.toHexString()}`);
// Save token for reconnection — keyed per server/database
localStorage.setItem(TOKEN_KEY, token);
})
.onAutomaticReconnect((_conn, identity, token) => {
console.log(`Reconnected! Identity: ${identity.toHexString()}`);
localStorage.setItem(TOKEN_KEY, token);
})
.onConnectError((_ctx, error, attempt, delayMs) => {
console.error('Connection failed:', error);
if (attempt !== undefined) console.log(`Retry ${attempt} in ${delayMs} ms`);
})
.onDisconnect((_ctx, error, attempt, delayMs) => {
if (attempt !== undefined) {
console.log(`Connection lost; retry ${attempt} in ${delayMs} ms`, error);
} else {
console.log('Connection ended', error);
}
})
.build();var conn = DbConnection.Builder()
.WithUri("https://maincloud.spacetimedb.com")
.WithDatabaseName("my_database")
.WithAutomaticReconnect()
.OnConnect((conn, identity, token) =>
{
Console.WriteLine($"Connected! Identity: {identity}");
// Save token for reconnection
})
.OnAutomaticReconnect((conn, identity, token) =>
{
Console.WriteLine($"Reconnected! Identity: {identity}");
// Save the retained or refreshed token
})
.OnConnectError((error, next) =>
{
Console.WriteLine($"Connection failed: {error}");
if (next is { } retry)
{
Console.WriteLine($"Retry {retry.Attempt} in {retry.Delay.TotalMilliseconds} ms");
}
})
.OnDisconnect((conn, error, next) =>
{
if (next is { } retry)
{
Console.WriteLine($"Connection lost; retry {retry.Attempt} in {retry.Delay.TotalMilliseconds} ms: {error}");
}
else
{
Console.WriteLine($"Connection ended: {error}");
}
})
.Build();let conn = DbConnection::builder()
.with_uri("https://maincloud.spacetimedb.com")
.with_database_name("my_database")
.on_connect(|_ctx, _identity, token| {
println!("Connected! Saving token...");
// Save token for reconnection
})
.on_connect_error(|_ctx, error| {
eprintln!("Connection failed: {}", error);
})
.on_disconnect(|_ctx, error| {
if let Some(err) = error {
eprintln!("Disconnected with error: {}", err);
} else {
println!("Disconnected normally");
}
})
.build()
.expect("Failed to connect");// Create delegates
FOnConnectDelegate ConnectDelegate;
BIND_DELEGATE_SAFE(ConnectDelegate, this, AMyActor, OnConnected);
FOnConnectErrorDelegate ErrorDelegate;
BIND_DELEGATE_SAFE(ErrorDelegate, this, AMyActor, OnConnectError);
FOnDisconnectDelegate DisconnectDelegate;
BIND_DELEGATE_SAFE(DisconnectDelegate, this, AMyActor, OnDisconnected);
// Build connection with callbacks
UDbConnection* Conn = UDbConnection::Builder()
->WithUri(TEXT("https://maincloud.spacetimedb.com"))
->WithDatabaseName(TEXT("my_database"))
->OnConnect(ConnectDelegate)
->OnConnectError(ErrorDelegate)
->OnDisconnect(DisconnectDelegate)
->Build();
// Callback functions (must be UFUNCTION)
UFUNCTION()
void OnConnected(UDbConnection* Connection, FSpacetimeDBIdentity Identity, const FString& Token)
{
UE_LOG(LogTemp, Log, TEXT("Connected! Identity: %s"), *Identity.ToHexString());
// Save token for reconnection
}
UFUNCTION()
void OnConnectError(const FString& Error)
{
UE_LOG(LogTemp, Error, TEXT("Connection failed: %s"), *Error);
}
UFUNCTION()
void OnDisconnected(UDbConnection* Connection, const FString& Error)
{
UE_LOG(LogTemp, Warning, TEXT("Disconnected from SpacetimeDB: %s"), *Error);
}Disconnecting
Explicitly close the connection when you're done:
- TypeScript
- C#
- Rust
- Unreal
conn.disconnect();conn.Disconnect();conn.disconnect();Conn->Disconnect();Reconnection Behavior
- TypeScript
- C#
- Rust
- Unreal
Enable .withAutomaticReconnect() to recover after an established connection drops. The SDK retains the connection, cached rows, subscription handles, and callbacks, then restores subscriptions automatically. While isReconnecting is true, cache reads return the last known data.
Register subscriptions and row callbacks in onConnect, which runs only once, or once after build(). Successful recovery invokes onAutomaticReconnect(conn, identity, token) before subscription replay; wait for subscription onApplied callbacks when you need refreshed data. Save authentication tokens in both connection callbacks.
onDisconnect and onConnectError report the upcoming retry attempt and delay, or undefined when no retry is scheduled. Initial connection failures are not retried, and disconnect() stops recovery. For expiring credentials, combine .withToken(initialToken) with .withTokenProvider(() => auth.getAccessToken()) to refresh tokens when needed before reconnect attempts.
React, Solid, and Svelte providers enable automatic reconnect through their shared connection manager. Vue and Angular require .withAutomaticReconnect() on the provider's builder. Without automatic reconnect, create a new connection after a connection loss. See the TypeScript reference for backoff options, pending-call behavior, and server requirements.
Enable .WithAutomaticReconnect() to recover after an established connection drops. The SDK retains the connection, cached rows, subscription handles, and callbacks, then restores subscriptions in one batch. While IsReconnecting is true, cache reads return the last known data. Keep calling FrameTick() during outages, even while IsActive is false. Unity's SpacetimeDBNetworkManager handles this automatically.
Register subscriptions and row callbacks in OnConnect, which runs only once, or once after Build(). Successful recovery invokes OnAutomaticReconnect(conn, identity, token) before subscription replay; wait for subscription OnApplied callbacks when you need refreshed data. Save authentication tokens in both connection callbacks.
The OnDisconnect((conn, error, next) => ...) and OnConnectError((error, next) => ...) overloads report the upcoming retry's Attempt and Delay, or null when no retry is scheduled. Initial connection failures are not retried, and Disconnect() stops recovery. For expiring credentials, combine .WithToken(initialToken) with .WithTokenProvider(() => RefreshTokenAsync()) to refresh tokens when needed before reconnect attempts.
Without automatic reconnect, create a new connection after a connection loss. See the C# reference for backoff options, token-provider requirements, and pending-call behavior.
Automatic reconnect support is coming soon. For now, implement reconnection in your application: create a new connection and restore subscriptions after a connection loss.
Automatic reconnect support for the Unreal C++ SDK is coming soon. For now, implement reconnection in your application: create a new connection and restore subscriptions after a connection loss.
Connection Identity
Every connection receives a unique identity from the server. Access it through the on_connect callback:
- TypeScript
- C#
- Rust
- Unreal
.onConnect((conn, identity, token) => {
console.log(`Identity: ${identity.toHexString()}, ConnectionId: ${conn.connectionId}`);
}).OnConnect((conn, identity, token) =>
{
var connectionId = conn.ConnectionId;
Console.WriteLine($"Identity: {identity}, ConnectionId: {connectionId}");
}).on_connect(|ctx, identity, token| {
let connection_id = ctx.connection_id();
println!("Identity: {:?}, ConnectionId: {:?}", identity, connection_id);
})UFUNCTION()
void OnConnected(UDbConnection* Connection, FSpacetimeDBIdentity Identity, const FString& Token)
{
FSpacetimeDBConnectionId ConnectionId = Connection->GetConnectionId();
UE_LOG(LogTemp, Log, TEXT("Identity: %s, ConnectionId: %s"),
*Identity.ToHexString(), *ConnectionId.ToHexString());
}The identity persists across connections and represents the user, while the connection ID is unique to each connection session.
Next Steps
Now that you have a connection established, you can:
- Use the SDK API to interact with tables, invoke reducers, and subscribe to data
- Register callbacks for observing database changes
- Call reducers and procedures on the server
For language-specific details, see: