Skip to main content
Lark supports conditional writes using ETags. This gives you compare-and-swap (CAS) semantics over HTTP — write only if the data hasn’t changed since you last read it.

How it works

  1. Read data and request its ETag.
  2. When you’re ready to write, include the ETag in your request.
  3. If the data has changed since you read it (ETag mismatch), the write fails with 412 Precondition Failed.
  4. If the data is unchanged, the write succeeds.
This prevents lost updates when multiple clients or servers are writing to the same path.

Getting an ETag

Add the X-Firebase-ETag: true header to any GET request:
curl -i \
  -H 'X-Firebase-ETag: true' \
  'https://chess-app.larkdb.net/my-game/players/alice.json?auth=YOUR_TOKEN'
Response headers include the ETag:
HTTP/2 200
ETag: "a1b2c3d4e5f6..."
Content-Type: application/json

{"name":"Alice","score":250,"online":true}
The ETag is a SHA-256 hash of the data’s canonical JSON representation.

Conditional write

Include the ETag in an If-Match header on your write:
curl -X PUT \
  -H 'If-Match: "a1b2c3d4e5f6..."' \
  'https://chess-app.larkdb.net/my-game/players/alice/score.json?auth=YOUR_TOKEN' \
  -d '300'

Success

If the ETag matches (data hasn’t changed), the write succeeds normally:
HTTP/2 200

300

Conflict

If someone else modified the data between your read and write, you get a 412:
HTTP/2 412

{"error":"condition_failed","message":"ETag mismatch"}
The response body contains the current value and the response includes the updated ETag, so you can read the new state and retry.

Retry pattern

A typical conditional update loop:
async function incrementScore(projectUrl: string, path: string, token: string) {
  const url = `${projectUrl}/${path}.json?auth=${token}`;

  while (true) {
    // 1. Read with ETag
    const readResponse = await fetch(url, {
      headers: { 'X-Firebase-ETag': 'true' }
    });
    const etag = readResponse.headers.get('ETag');
    const currentValue = await readResponse.json();

    // 2. Compute new value
    const newValue = (currentValue ?? 0) + 1;

    // 3. Conditional write
    const writeResponse = await fetch(url, {
      method: 'PUT',
      headers: { 'If-Match': etag! },
      body: JSON.stringify(newValue)
    });

    if (writeResponse.ok) {
      return newValue; // Success
    }

    if (writeResponse.status === 412) {
      continue; // Conflict — retry with fresh data
    }

    throw new Error(`Unexpected status: ${writeResponse.status}`);
  }
}

When to use conditional requests

Conditional writes are useful for:
  • Counters and balances — Increment a value without risking double-counts.
  • Optimistic locking — Let a user edit data and reject stale writes.
  • Server-side updates — Backend services that read-modify-write without holding a persistent connection.
For client-side compare-and-swap, transactions in the Lark SDK provide a more ergonomic API with automatic retries.