Function: withWriterKey()

withWriterKey<DB, T>(db: Kysely<DB>, writer: null | string, fn: (trx: Kysely<DB>) => Promise<T>): Promise<T>

Runs a block of DML with a transaction-scoped writer identity and restores the previous value afterwards (nesting-safe).

Many clients (for example rich‑text editors) need to react to external changes to the same document while suppressing their own writes to avoid feedback loops. The state API supports this by stamping a writer_key for each INSERT, UPDATE, and DELETE. Observers can then filter “not me”.

This helper provides the single recommended way to pass the writer identity for a batch of mutations: it starts a transaction on the provided connection, sets the writer via lix_set_writer_key(<writer|null>), executes your callback with a transaction-bound Kysely instance, and restores the previous writer (or NULL) in a finally block. The engine also clears the writer on COMMIT/ROLLBACK; the restore step handles nested usage gracefully.

Use a session-scoped writer value (for example flashtype:tiptap#<sessionId>) so that two browser tabs don’t suppress each other’s updates. Do not set writer_key columns directly in DML — the engine manages them.

Example

// Upsert and delete with the same writer (editor persistence)
await withWriterKey(db, 'flashtype:tiptap#<sessionId>', async (trx) => {
  await trx
    .insertInto('state')
    .values({ file_id, version_id, entity_id, schema_key, plugin_key, schema_version, snapshot_content: snapshot as any })
    .onConflict((oc) => oc.columns(['file_id','version_id','entity_id','schema_key']).doUpdateSet({ snapshot_content: snapshot as any }))
    .execute();

  await trx
    .deleteFrom('state')
    .where('file_id','=',fileId)
    .where('version_id','=',versionId)
    .where('entity_id','=',entityId)
    .where('schema_key','=',schemaKey)
    .execute();
});

Type Parameters

Type Parameter
DB
T

Parameters

ParameterType
dbKysely<DB>
writernull | string
fn(trx: Kysely<DB>) => Promise<T>

Returns

Promise<T>