Skip to main content

Constraints

Constraints enforce data integrity rules on your tables. SpacetimeDB supports primary key and unique constraints.

Primary Keys

A primary key uniquely identifies each row in a table. It represents the identity of a row and determines how updates and deletes are handled.

import { table, t } from 'spacetimedb/server';

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

Use the .primaryKey() method on a column builder to mark it as the primary key.

Primary Key Rules

  • One per table: A table can have at most one primary key column.
  • Immutable identity: The primary key defines the row's identity. Changing a primary key value is treated as deleting the old row and inserting a new one.
  • Unique by definition: Primary keys are automatically unique. No two rows can have the same primary key value.

Because of the unique constraint, SpacetimeDB implements primary keys using a unique index. This index is created automatically.

Multi-Column Primary Keys

SpacetimeDB does not yet support multi-column (composite) primary keys. If you need to look up rows by multiple columns, use a multi-column btree index combined with an auto-increment primary key:

const inventory = table(
  {
    name: 'inventory',
    public: true,
    indexes: [
      { name: 'by_user_item', algorithm: 'btree', columns: ['userId', 'itemId'] },
    ],
  },
  {
    id: t.u64().primaryKey().autoInc(),
    userId: t.u64(),
    itemId: t.u64(),
    quantity: t.u32(),
  }
);

This gives you efficient lookups by the column combination while using a simple auto-increment value as the primary key.

Updates and Primary Keys

When you update a row, SpacetimeDB uses the primary key to determine whether it's a modification or a replacement:

  • Same primary key: The row is updated in place. Subscribers see an update event.
  • Different primary key: The old row is deleted and a new row is inserted. Subscribers see a delete event followed by an insert event.
spacetimedb.reducer('update_user_name', { id: t.u64(), newName: t.string() }, (ctx, { id, newName }) => {
  const user = ctx.db.user.id.find(id);
  if (user) {
    // This is an update — primary key (id) stays the same
    ctx.db.user.id.update({ ...user, name: newName });
  }
});

Tables Without Primary Keys

Tables don't require a primary key. Without one, the entire row acts as the primary key:

  • Rows are identified by their complete content
  • Updates require matching all fields
  • Duplicate rows are not possible. Inserting an identical row has no effect

SpacetimeDB always maintains set semantics regardless of whether you define a primary key. The difference is what defines uniqueness: a primary key column, or the entire row.

Primary keys add indexing overhead. If your table is only accessed by iterating over all rows (no lookups by key), omitting the primary key can improve performance.

Common Primary Key Patterns

Auto-incrementing IDs: Combine primaryKey() with autoInc() for automatically assigned unique identifiers:

#[spacetimedb::table(name = post, public)]
pub struct Post {
    #[primary_key]
    #[auto_inc]
    id: u64,
    title: String,
    content: String,
}

Identity as primary key: Use the caller's identity as the primary key for user-specific data:

#[spacetimedb::table(name = user_profile, public)]
pub struct UserProfile {
    #[primary_key]
    identity: Identity,
    display_name: String,
    bio: String,
}

This pattern ensures each identity can only have one profile and makes lookups by identity efficient.

Unique Columns

Mark columns as unique to ensure no two rows can have the same value for that column.

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

Use the .unique() method on a column builder.

Unlike primary keys, you can have multiple unique columns on a single table. Unique columns also create an index that enables efficient lookups.

Primary Keys vs Unique Columns

Both primary keys and unique columns enforce uniqueness, but they serve different purposes:

AspectPrimary KeyUnique Column
PurposeRow identityData integrity
Count per tableOneMultiple allowed
Update behaviorDelete + InsertIn-place update
RequiredNoNo