v145 · Web APIs · Storage

Upsert

Chrome 145 adds an "upsert" operation to the IndexedDB object store — a combined insert-or-update that atomically writes a record, creating it if absent or updating it if present, without a separate read-modify-write cycle.

background

IndexedDB's existing put() method already performs an upsert semantically (insert if absent, replace if present). The Chrome 145 "Upsert" feature formalises and exposes a more explicit, spec-aligned upsert operation that is also consistent with emerging Web Storage patterns and SQLite's INSERT OR REPLACE semantics.

The primary benefit is atomic, conflict-free writes — useful when multiple tabs or workers may write to the same key concurrently, as upsert avoids the read-check-write race condition.

concepts

  1. Upsert Demo

    Interactive demo showing put() in upsert mode — writing a record, updating it, and observing that no pre-read is required.

  2. API Patterns

    Common patterns — idempotent writes, counter increments, partial updates — and how upsert simplifies each compared to the read-modify-write alternative.

  3. Concurrency Stress

    Race many concurrent writers against the same counter — read-modify-write vs. atomic upsert — and watch the lost-update count climb on one side and stay flat on the other.

  4. Offline Sync

    An offline-first notes app that writes to IndexedDB immediately, then syncs on reconnect. Because sync uses put() in upsert mode, sending the same note twice is idempotent — no duplicates, no read-before-write needed.

the change

// IndexedDB put() is upsert: insert if absent, replace if present
const tx = db.transaction('settings', 'readwrite');
const store = tx.objectStore('settings');

// Upsert: no pre-read needed — atomic write
store.put({ id: 'theme', value: 'dark', updatedAt: Date.now() });

// Before Chrome 145 pattern (verbose, not atomic):
const existingReq = store.get('theme');
existingReq.onsuccess = () => {
  const existing = existingReq.result;
  store.put({ ...existing, value: 'dark', updatedAt: Date.now() });
};

// Chrome 145 upsert spec alignment: put() behaviour formalised
// to match INSERT OR REPLACE semantics — same key = full replace.

references