Github|...

Mutations

Create, update and delete. Writes land locally first, then sync. Your UI never waits on the network.

Mutating data in Sp00ky is performant and secure. Changes are instantly reflected in your local UI (optimistic updates) and synchronized with the server.

Creating Records

To create a new record, use the db.create method. You need to specify the record ID (or let Sp00ky generate one) and the data.

import { RecordId, Uuid } from 'surrealdb';

// Generate a unique ID
const genId = Uuid.v4().toString().replace(/-/g, '');
const threadId = \`thread:\${genId}\`;

// Create the record. `db.create` takes a fully-qualified record id
// ("thread:abc...") plus a typed payload. There's no separate table
// argument. Pass record-typed fields as `RecordId` instances so the
// SurrealDB SDK encodes them correctly.
await db.create(threadId, {
  title: 'My New Thread',
  active: true,
  author: new RecordId('user', currentUserId),
});
Warning

Ensure you adhere to the PERMISSIONS defined in your schema. If a user does not have permission to create a record, the operation will fail.

Updating Records

Use db.update() to update existing records. This performs a partial update (merge) by default.

// Partial update
await db.update('thread', 'thread:123', {
  title: 'Updated Title'
});

// Update with debouncing (useful for real-time typing)
await db.update('thread', 'thread:123', {
  content: 'Updated content...'
}, {
  debounced: {
    key: 'recordId_x_fields',
    delay: 500
  }
});

Debounced updates and leaving the page

A debounced update changes the local row at once, but the outbox entry that pushes it waits for delay. By default (flushOnHide: true) that wait is safe to interrupt:

  • Every call also stores the merged patch in the local _00_pending_writes table, in the same transaction as the row itself.
  • When the page is hidden (tab switch, reload, closing the tab, the app going to the background) every pending patch moves into the outbox and is pushed right away, without waiting out delay.
  • If the page is gone before that finishes, the patch is still in the local store. The next time the app boots (or another open tab) moves it into the outbox, a few seconds after its delay would have ended, and it syncs like any other queued write.

Turn it off for values you are happy to lose on reload:

// A throwaway value: skip the durable copy, lose it on reload
await db.update('thread', 'thread:123', {
  cursor: 42
}, {
  debounced: { delay: 500, flushOnHide: false }
});
Note

A browser gives no guarantee that a network request started while the page unloads completes, so the push on reload or close is best effort. The local copy is what guarantees the write is not lost; it reaches the server on the next start.

Deleting Records

To delete a record, call db.delete with the table name and record ID.

await db.delete('thread', 'thread:123');