docs/docs/00200-core-concepts/00600-clients/00200-codegen.md
import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';
Before you can interact with a SpacetimeDB database from a client application, you must generate client bindings for your module. These bindings create type-safe interfaces that allow your client to query tables, invoke reducers, call procedures, and subscribe to tables, and/or views.
Module bindings are auto-generated code that mirrors your module's schema and functions. They provide:
The bindings ensure compile-time type safety between your client and server code, catching errors before runtime.
Use the spacetime generate command to create bindings from your module:
mkdir -p src/module_bindings
spacetime generate --lang typescript --out-dir src/module_bindings --module-path PATH-TO-MODULE-DIRECTORY
This generates TypeScript files in src/module_bindings/. Import them in your client:
import * as moduleBindings from './module_bindings';
Replace PATH-TO-MODULE-DIRECTORY with the path to your module's directory, where the module's package.json is located.
mkdir -p module_bindings
spacetime generate --lang cs --out-dir module_bindings --module-path PATH-TO-MODULE-DIRECTORY
This generates C# files in module_bindings/. The generated files are automatically included in your project.
Replace PATH-TO-MODULE-DIRECTORY with the path to your module's directory, where the module's .csproj is located.
mkdir -p src/module_bindings
spacetime generate --lang rust --out-dir client/src/module_bindings --module-path PATH-TO-MODULE-DIRECTORY
This generates Rust files in client/src/module_bindings/. Import them in your client with:
mod module_bindings;
Replace PATH-TO-MODULE-DIRECTORY with the path to your module's directory, where the module's Cargo.toml is located.
spacetime generate --lang unrealcpp --uproject-dir PATH-TO-UPROJECT --module-path PATH-TO-MODULE-DIRECTORY --unreal-module-name YOUR_MODULE_NAME
This generates Unreal C++ files in your project's ModuleBindings directory. The generated files are automatically included in your Unreal project.
Replace:
.uproject file)The spacetime generate command creates client-side representations of your module's components:
For each table in your module, codegen generates:
DbConnection for querying the client cacheFor example, a user table becomes:
// Generated type
export default __t.object("User", {
id: __t.u64(),
name: __t.string(),
email: __t.string(),
});
// Access via DbConnection
conn.db.User
// Generated type
public partial class User
{
public ulong Id;
public string Name;
public string Email;
}
// Access via DbConnection
conn.Db.User
// Generated type
pub struct User {
pub id: u64,
pub name: String,
pub email: String,
}
// Access via DbConnection
conn.db().user()
// Generated type
USTRUCT(BlueprintType)
struct FUser
{
GENERATED_BODY()
UPROPERTY(BlueprintReadWrite)
int64 Id;
UPROPERTY(BlueprintReadWrite)
FString Name;
UPROPERTY(BlueprintReadWrite)
FString Email;
};
// Access via DbConnection
Context.Db->User
See the Tables documentation for details on defining tables in your module.
For each reducer in your module, codegen generates:
For example, a create_user reducer becomes:
// Call the reducer
conn.reducers.createUser(name, email);
// Register a callback to observe reducer invocations
conn.reducers.onCreateUser((ctx, name, email) => {
console.log(`User created: ${name}`);
});
// Call the reducer
conn.Reducers.CreateUser(name, email);
// Register a callback to observe reducer invocations
conn.Reducers.OnCreateUser += (ctx, name, email) =>
{
Console.WriteLine($"User created: {name}");
};
// Call the reducer
conn.reducers().create_user(name, email);
// Register a callback to observe reducer invocations
conn.reducers().on_create_user(|ctx, name, email| {
println!("User created: {}", name);
});
// Call the reducer
Context.Reducers->CreateUser(TEXT("Alice"), TEXT("[email protected]"));
// Register a callback to observe reducer invocations
FOnCreateUserDelegate Callback;
BIND_DELEGATE_SAFE(Callback, this, AMyActor, OnCreateUser);
Context.Reducers->OnCreateUser(Callback);
// Callback function (must be UFUNCTION)
UFUNCTION()
void OnCreateUser(const FReducerEventContext& Ctx, const FString& Name, const FString& Email)
{
UE_LOG(LogTemp, Log, TEXT("User created: %s"), *Name);
}
See the Reducers documentation for details on defining reducers in your module.
For each procedure in your module, codegen generates:
Procedures are currently in beta. For example, a fetch_external_data procedure becomes:
// Call the procedure
conn.procedures
.fetchExternalData(url)
.then(result => console.log(`Got result: ${result}`))
.catch(error => console.error(`Error: ${error}`));
// Call the procedure without a callback
conn.Procedures.FetchExternalData(url);
// Call the procedure with a callback for the result
conn.Procedures.FetchExternalData(url, (ctx, result) =>
{
if (result.IsSuccess)
{
Console.WriteLine($"Got result: {result.Value!}");
}
else
{
Console.WriteLine($"Error: {result.Error!}");
}
});
// Call the procedure without a callback
conn.procedures().fetch_external_data(url);
// Call the procedure with a callback for the result
conn.procedures().fetch_external_data_then(url, |ctx, result| {
match result {
Ok(data) => println!("Got result: {:?}", data),
Err(error) => eprintln!("Error: {:?}", error),
}
});
// Call the procedure without a callback
Context.Procedures->FetchExternalData(url, {});
// Call the procedure with a callback for the result
FOnFetchExternalDataComplete Callback;
BIND_DELEGATE_SAFE(Callback, this, AMyActor, OnFetchComplete);
Context.Procedures->FetchExternalData(url, Callback);
// Callback function (must be UFUNCTION)
UFUNCTION()
void OnFetchComplete(const FProcedureEventContext& Ctx, const FString& Result, bool bSuccess)
{
if (bSuccess)
{
UE_LOG(LogTemp, Log, TEXT("Got result: %s"), *Result);
}
else
{
UE_LOG(LogTemp, Error, TEXT("Error"));
}
}
See the Procedures documentation for details on defining procedures in your module.
For each view in your module, codegen generates:
Views provide subscribable, computed queries over your data.
See the Views documentation for details on defining views in your module.
Whenever you modify your module's schema or function signatures, regenerate the client bindings by running spacetime generate again. The tool will overwrite the existing generated files with updated code.
If you're actively developing and testing changes, consider adding spacetime generate to your build or development workflow.
Once you've generated the bindings, you're ready to connect to your database and start interacting with it. See:
If spacetime generate fails to find your module, ensure you're pointing to the directory containing your module's project file (Cargo.toml, .csproj, or package.json).
If your client doesn't see new tables or reducers, ensure you've regenerated the bindings after updating your module. Generated code is not automatically updated when the module changes.