CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-idb

A small wrapper that makes IndexedDB usable with promises and enhanced TypeScript support

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

transaction-management.mddocs/

Transaction Management

Enhanced transaction interface with automatic completion handling and convenient store access.

Capabilities

Transaction Properties

Access transaction metadata and associated objects.

/**
 * The transaction's mode
 */
readonly mode: Mode;

/**
 * The names of stores in scope for this transaction
 */
readonly objectStoreNames: TypedDOMStringList<TxStores[number]>;

/**
 * The transaction's connection
 */
readonly db: IDBPDatabase<DBTypes>;

/**
 * Promise for the completion of this transaction
 * Resolves when transaction completes successfully
 * Rejects if transaction is aborted or encounters an error
 */
readonly done: Promise<void>;

/**
 * The associated object store, if the transaction covers a single store
 * Undefined if transaction covers multiple stores
 */
readonly store: TxStores[1] extends undefined
  ? IDBPObjectStore<DBTypes, TxStores, TxStores[0], Mode>
  : undefined;

Usage Examples:

import { openDB } from "idb";

const db = await openDB("my-database");

// Single store transaction
const tx = db.transaction("users", "readwrite");
console.log("Transaction mode:", tx.mode); // "readwrite"
console.log("Store names:", [...tx.objectStoreNames]); // ["users"]

// Access single store directly
const userStore = tx.store; // Available because only one store
await userStore.add({ name: "Alice" });
await tx.done; // Wait for transaction completion

// Multiple store transaction
const tx2 = db.transaction(["users", "posts"], "readwrite");
console.log("Store names:", [...tx2.objectStoreNames]); // ["users", "posts"]
console.log("Direct store access:", tx2.store); // undefined (multiple stores)

// Must access stores explicitly
const userStore2 = tx2.objectStore("users");
const postStore = tx2.objectStore("posts");

Object Store Access

Get object stores within the transaction scope.

/**
 * Returns an IDBObjectStore in the transaction's scope
 * @param name - Name of the object store
 * @returns Enhanced object store interface
 */
objectStore<StoreName extends TxStores[number]>(
  name: StoreName
): IDBPObjectStore<DBTypes, TxStores, StoreName, Mode>;

Usage Examples:

// Single store transaction with explicit access
const tx = db.transaction("users", "readwrite");
const userStore = tx.objectStore("users");
await userStore.add({ name: "Bob" });

// Multiple store transaction
const tx2 = db.transaction(["users", "posts"], "readwrite");
const userStore2 = tx2.objectStore("users");
const postStore = tx2.objectStore("posts");

// Add user and their first post in same transaction
const userId = await userStore2.add({ name: "Charlie", email: "charlie@example.com" });
await postStore.add({ title: "My First Post", authorId: userId });

await tx2.done; // Both operations complete together

Transaction Completion

Handle transaction completion and error scenarios.

/**
 * Promise that resolves when the transaction completes successfully
 * Rejects if the transaction is aborted or encounters an error
 */
readonly done: Promise<void>;

Transaction Completion Examples:

// Basic transaction completion
const tx = db.transaction("users", "readwrite");
try {
  await tx.store.add({ name: "Alice" });
  await tx.store.add({ name: "Bob" });
  await tx.done; // Wait for both operations to complete
  console.log("Transaction completed successfully");
} catch (error) {
  console.log("Transaction failed:", error);
}

// Multiple operations with error handling
const tx2 = db.transaction(["users", "posts"], "readwrite");
const userStore = tx2.objectStore("users");
const postStore = tx2.objectStore("posts");

try {
  // These operations are atomic - all succeed or all fail
  const userId = await userStore.add({ name: "Dave" });
  await postStore.add({ title: "Post 1", authorId: userId });
  await postStore.add({ title: "Post 2", authorId: userId });
  
  await tx2.done; // Ensure everything is committed
  console.log("User and posts created successfully");
} catch (error) {
  console.log("Failed to create user and posts:", error);
  // All operations are rolled back automatically
}

// Transaction with conditional operations
const tx3 = db.transaction(["users", "settings"], "readwrite");
try {
  const existingUser = await tx3.objectStore("users").get("user123");
  
  if (!existingUser) {
    await tx3.objectStore("users").add({ id: "user123", name: "New User" });
    await tx3.objectStore("settings").add({ userId: "user123", theme: "dark" });
  } else {
    await tx3.objectStore("users").put({ ...existingUser, lastLogin: new Date() });
  }
  
  await tx3.done;
} catch (error) {
  console.log("Transaction failed:", error);
}

Transaction Options

Configure transaction behavior with durability and other options.

interface IDBTransactionOptions {
  /**
   * The durability of the transaction
   * - "default": Browser's default durability behavior
   * - "strict": Ensures data is written to disk before completion
   * - "relaxed": Better performance with fewer guarantees
   */
  durability?: 'default' | 'strict' | 'relaxed';
}

Transaction Options Examples:

// High durability for critical data
const criticalTx = db.transaction("financial_records", "readwrite", {
  durability: "strict"
});
await criticalTx.store.add({ amount: 1000, type: "deposit" });
await criticalTx.done; // Guaranteed to be written to disk

// Better performance for temporary data
const cacheTx = db.transaction("cache", "readwrite", {
  durability: "relaxed"
});
await cacheTx.store.put({ key: "temp_data", value: someData });
await cacheTx.done; // May complete before writing to disk

// Default durability (most common)
const normalTx = db.transaction("users", "readwrite", {
  durability: "default"
});
await normalTx.store.add({ name: "User" });
await normalTx.done;

Advanced Transaction Patterns

Complex transaction workflows and patterns.

Batch Operations:

async function batchInsertUsers(users: User[]) {
  const tx = db.transaction("users", "readwrite");
  const userStore = tx.store;
  
  // Start all operations without awaiting
  const promises = users.map(user => userStore.add(user));
  
  // Wait for all operations to complete
  await Promise.all([...promises, tx.done]);
  
  console.log(`Successfully added ${users.length} users`);
}

Transaction Rollback on Custom Conditions:

async function transferBetweenAccounts(fromId: string, toId: string, amount: number) {
  const tx = db.transaction("accounts", "readwrite");
  const accountStore = tx.store;
  
  try {
    const fromAccount = await accountStore.get(fromId);
    const toAccount = await accountStore.get(toId);
    
    if (!fromAccount || !toAccount) {
      throw new Error("Account not found");
    }
    
    if (fromAccount.balance < amount) {
      throw new Error("Insufficient funds");
    }
    
    // Update balances
    await accountStore.put({
      ...fromAccount,
      balance: fromAccount.balance - amount
    });
    
    await accountStore.put({
      ...toAccount,
      balance: toAccount.balance + amount
    });
    
    await tx.done; // Commit the transaction
    console.log("Transfer completed successfully");
    
  } catch (error) {
    // Transaction automatically rolls back on error
    console.log("Transfer failed:", error.message);
    throw error;
  }
}

Long-running Transactions with Progress Tracking:

async function processLargeDataset(data: any[]) {
  const batchSize = 100;
  const totalBatches = Math.ceil(data.length / batchSize);
  
  for (let i = 0; i < totalBatches; i++) {
    const batch = data.slice(i * batchSize, (i + 1) * batchSize);
    const tx = db.transaction("processed_data", "readwrite");
    
    try {
      for (const item of batch) {
        await tx.store.add(processItem(item));
      }
      
      await tx.done;
      console.log(`Processed batch ${i + 1}/${totalBatches}`);
      
    } catch (error) {
      console.log(`Failed to process batch ${i + 1}:`, error);
      throw error;
    }
  }
}

Transaction Lifecycle

Understanding transaction states and lifecycle management.

// Transaction begins when created
const tx = db.transaction("users", "readwrite");

// Transaction is active during operations
const userStore = tx.store;
await userStore.add({ name: "Alice" });

// Transaction can be explicitly aborted
// tx.abort(); // Would cause tx.done to reject

// Transaction completes when all operations finish
// and no more operations are queued
await tx.done; // Transaction is now complete

// Attempting operations after completion throws error
// await userStore.add({ name: "Bob" }); // Would throw TransactionInactiveError

docs

cursor-navigation.md

database-operations.md

enhanced-database.md

index-operations.md

index.md

object-store-operations.md

promise-wrapping.md

transaction-management.md

tile.json