Skip to main content

Column Types and Constraints

Columns define the structure of your tables. Each column has a type and can have constraints that enforce data integrity.

Column Types

SpacetimeDB supports a variety of column types optimized for performance.

Primitive Types

TypeReturnsTypeScript TypeDescription
t.bool()BoolBuilderbooleanBoolean value
t.string()StringBuilderstringUTF-8 string
t.f32()F32Buildernumber32-bit floating point
t.f64()F64Buildernumber64-bit floating point
t.i8()I8BuildernumberSigned 8-bit integer
t.u8()U8BuildernumberUnsigned 8-bit integer
t.i16()I16BuildernumberSigned 16-bit integer
t.u16()U16BuildernumberUnsigned 16-bit integer
t.i32()I32BuildernumberSigned 32-bit integer
t.u32()U32BuildernumberUnsigned 32-bit integer
t.i64()I64BuilderbigintSigned 64-bit integer
t.u64()U64BuilderbigintUnsigned 64-bit integer
t.i128()I128BuilderbigintSigned 128-bit integer
t.u128()U128BuilderbigintUnsigned 128-bit integer
t.i256()I256BuilderbigintSigned 256-bit integer
t.u256()U256BuilderbigintUnsigned 256-bit integer

Special Types

Structured Types

TypeReturnsTypeScript TypeDescription
t.object(name, obj)ProductBuilder<Obj>{ [K in keyof Obj]: T<Obj[K]> }Product/object type for nested or structured data
t.row(obj)RowBuilder<Obj>{ [K in keyof Obj]: T<Obj[K]> }Row type for table schemas (allows column metadata)
t.enum(name, variants)SumBuilder<Obj> or SimpleSumBuilder{ tag: 'variant' } | { tag: 'variant', value: T }Sum/enum type (tagged union or simple enum)
t.array(element)ArrayBuilder<Element>T<Element>[]Array of the given element type
t.unit()UnitBuilder{} or undefinedZero-field product type (unit)
t.option(value)OptionBuilder<Value>Value | undefinedOptional value type

Special Types

TypeReturnsTypeScript TypeDescription
t.identity()IdentityBuilderIdentityUnique identity for authentication
t.connectionId()ConnectionIdBuilderConnectionIdClient connection identifier
t.timestamp()TimestampBuilderTimestampAbsolute point in time (microseconds since Unix epoch)
t.timeDuration()TimeDurationBuilderTimeDurationRelative duration in microseconds
t.scheduleAt()ColumnBuilder<ScheduleAt, …>ScheduleAtSpecial column type for scheduling reducer execution

Column Constraints

Unique Columns

Mark columns as unique to ensure only one row can exist with a given value.

const user = table(
  { name: 'user', public: true },
  {
    id: t.u32().primaryKey(),
    email: t.string().unique(),
    username: t.string().unique(),
  }
);

Primary Key

A table can have one primary key column. The primary key represents the identity of the row. Changes that don't affect the primary key are updates; changes to the primary key are treated as delete + insert.

Only one column can be marked as a primary key, but multiple columns can be marked unique.

Auto-Increment Columns

Use auto-increment for automatically increasing integer identifiers. Inserting a row with a zero value causes the database to assign a new unique value.

const post = table(
  { name: 'post', public: true },
  {
    id: t.u64().primaryKey().autoInc(),
    title: t.string(),
  }
);

const spacetimedb = schema(post);

spacetimedb.reducer('add_post', { title: t.string() }, (ctx, { title }) => {
  const inserted = ctx.db.post.insert({ id: 0, title });
  // inserted.id now contains the assigned auto-incremented value
});

Default Values

Default values allow you to add new columns to existing tables during automatic migrations. When you republish a module with a new column that has a default value, existing rows are automatically populated with that default.

note

New columns with default values must be added at the end of the table definition. Adding columns in the middle of a table is not supported.

const player = table(
  { name: 'player', public: true },
  {
    id: t.u64().primaryKey().autoInc(),
    name: t.string(),
    // New columns added with defaults
    score: t.u32().default(0),
    isActive: t.bool().default(true),
    bio: t.string().default(''),
  }
);

The .default(value) method can be chained on any column type builder. The value must match the column's type.

Constraints

Default values cannot be combined with:

  • #[primary_key] / [PrimaryKey] / .primaryKey()
  • #[unique] / [Unique] / .unique()
  • #[auto_inc] / [AutoInc] / .autoInc()

This restriction exists because these constraints require the database to manage the column values, which conflicts with providing a static default.

TypeScript: Unintuitive Error Message

In TypeScript, this constraint is enforced at compile time. If you violate it, you'll see an unintuitive error message:

Expected 3 arguments, but got 2.

This error means one of your columns has an invalid combination of .default() with .primaryKey(), .unique(), or .autoInc().

For example, this code will produce the error:

// ERROR: default() + primaryKey() is not allowed
const badTable = table(
  { name: 'bad' },
  { id: t.u64().default(0n).primaryKey() }  // <- Causes "Expected 3 arguments"
);

How to fix: Remove either .default() or the constraint (.primaryKey()/.unique()/.autoInc()).

Use Cases

  • Schema evolution: Add new features to your application without losing existing data
  • Optional fields: Provide sensible defaults for fields that may not have been tracked historically
  • Feature flags: Add boolean columns with default(false) to enable new functionality gradually