Skip to content

IndexedDB

This lesson explains IndexedDB with clear examples, use cases, and best practices so you can use HTML5 APIs confidently in real web applications.

HTML5 IndexedDB API Overview

The HTML5 IndexedDB API is a browser-based database API used to store large amounts of structured data on the client side. Unlike Local Storage, IndexedDB supports objects, indexes, transactions, large datasets, offline storage, and asynchronous database operations.

IndexedDB is commonly used in Progressive Web Apps, offline-first applications, note apps, dashboards, e-commerce carts, form drafts, local caches, document editors, media applications, and apps that need to work without a constant internet connection.

Feature Description
API Name IndexedDB API
Main Object indexedDB
Storage Type Client-side NoSQL object database
Data Format JavaScript objects, files, blobs, arrays, and structured data
Programming Style Asynchronous event-based API
Best For Large offline data, caching, search indexes, drafts, PWA data

IndexedDB Core Concepts

IndexedDB uses databases, object stores, keys, indexes, and transactions to organize and manage data inside the browser.

Concept Description Example
Database Container for one or more object stores. AppDatabase
Object Store Similar to a table that stores records. users, products, drafts
Record A stored JavaScript object. { id: 1, name: "Ayaan" }
Key Unique identifier for a record. id
Index Searchable field inside an object store. email, category
Transaction Controls read or write operations. readonly, readwrite

Open IndexedDB Database

// Open IndexedDB database

const request =
  indexedDB.open("AppDatabase", 1);

request.onupgradeneeded = (event) => {
  const database =
    event.target.result;

  if (!database.objectStoreNames.contains("users")) {
    database.createObjectStore(
      "users",
      {
        keyPath: "id",
        autoIncrement: true
      }
    );
  }
};

request.onsuccess = (event) => {
  const database =
    event.target.result;

  console.log("Database opened:", database.name);
};

request.onerror = () => {
  console.error("Database failed to open.");
};

The indexedDB.open() method opens a database. The onupgradeneeded event runs when the database is created for the first time or when the version number increases.

IndexedDB Methods and Events

Method / Event Purpose Example Syntax
indexedDB.open() Opens or creates a database. indexedDB.open("AppDatabase", 1)
onupgradeneeded Creates or updates schema. request.onupgradeneeded = callback
onsuccess Runs when request succeeds. request.onsuccess = callback
onerror Runs when request fails. request.onerror = callback
transaction() Creates a transaction. database.transaction("users", "readwrite")
objectStore() Accesses an object store. transaction.objectStore("users")
add() Adds a new record. store.add(user)
put() Adds or updates a record. store.put(user)
get() Reads one record by key. store.get(1)
getAll() Reads all records. store.getAll()
delete() Deletes one record. store.delete(1)
clear() Deletes all records from a store. store.clear()

Add Data to IndexedDB

// Add user record

function addUser(database, user) {
  const transaction =
    database.transaction("users", "readwrite");

  const store =
    transaction.objectStore("users");

  const request =
    store.add(user);

  request.onsuccess = () => {
    console.log("User added successfully.");
  };

  request.onerror = () => {
    console.error("Failed to add user.");
  };
}

addUser(
  database,
  {
    name: "Ayaan",
    email: "ayaan@example.com",
    role: "Student"
  }
);

The add() method inserts a new record. If a record with the same key already exists, the request fails.

Read Data from IndexedDB

// Read one user by key

function getUser(database, id) {
  const transaction =
    database.transaction("users", "readonly");

  const store =
    transaction.objectStore("users");

  const request =
    store.get(id);

  request.onsuccess = () => {
    console.log("User:", request.result);
  };

  request.onerror = () => {
    console.error("Failed to read user.");
  };
}

getUser(database, 1);

Use get() to read a single record by key. Use getAll() when you need all records from an object store.

Read All Records

// Read all users

function getAllUsers(database) {
  const transaction =
    database.transaction("users", "readonly");

  const store =
    transaction.objectStore("users");

  const request =
    store.getAll();

  request.onsuccess = () => {
    console.log("Users:", request.result);
  };
}

getAllUsers(database);

The getAll() method returns all records from the selected object store.

Update Data with put()

// Update user record

function updateUser(database, user) {
  const transaction =
    database.transaction("users", "readwrite");

  const store =
    transaction.objectStore("users");

  const request =
    store.put(user);

  request.onsuccess = () => {
    console.log("User updated.");
  };
}

updateUser(
  database,
  {
    id: 1,
    name: "Ayaan Poluru",
    email: "ayaan@example.com",
    role: "Student"
  }
);

The put() method updates an existing record or creates it if it does not already exist.

Delete Data from IndexedDB

// Delete user record

function deleteUser(database, id) {
  const transaction =
    database.transaction("users", "readwrite");

  const store =
    transaction.objectStore("users");

  const request =
    store.delete(id);

  request.onsuccess = () => {
    console.log("User deleted.");
  };
}

deleteUser(database, 1);

Use delete() to remove one record by key. Use clear() to remove every record from an object store.

Create and Use Indexes

// Create index during upgrade

request.onupgradeneeded = (event) => {
  const database =
    event.target.result;

  const store =
    database.createObjectStore(
      "users",
      {
        keyPath: "id",
        autoIncrement: true
      }
    );

  store.createIndex(
    "email",
    "email",
    {
      unique: true
    }
  );
};
// Search by indexed email

function getUserByEmail(database, email) {
  const transaction =
    database.transaction("users", "readonly");

  const store =
    transaction.objectStore("users");

  const index =
    store.index("email");

  const request =
    index.get(email);

  request.onsuccess = () => {
    console.log(request.result);
  };
}

getUserByEmail(database, "ayaan@example.com");

Indexes allow fast lookup by fields other than the primary key, such as email, category, status, or created date.

IndexedDB Transactions

Transaction Mode Description Best Use
readonly Allows read-only operations. Search, display, read records.
readwrite Allows reading and writing. Add, update, delete records.
versionchange Used during schema upgrades. Create object stores and indexes.

Offline Cache Example

// Save API response to IndexedDB

async function cacheUsers(database) {
  const response =
    await fetch("/api/users");

  const users =
    await response.json();

  const transaction =
    database.transaction("users", "readwrite");

  const store =
    transaction.objectStore("users");

  users.forEach((user) => {
    store.put(user);
  });
}

This example fetches users from an API and stores them locally so the app can display cached data later when offline.

Common IndexedDB Use Cases

Use Case How IndexedDB Helps
Progressive Web Apps Stores app data locally for offline access.
Offline Forms Saves form drafts until network is available.
E-commerce Carts Keeps cart items available across sessions.
Media Apps Stores images, audio, videos, and Blob data.
Dashboards Caches large API datasets for faster loading.
Note Apps Saves notes locally before syncing to the server.
Search Indexes Stores indexed content for local search.
Document Editors Stores drafts, autosaves, and local document versions.

IndexedDB vs Local Storage vs Cookies

Feature IndexedDB Local Storage Cookies
Storage Size Large Small to medium Very small
Data Type Objects, files, blobs, structured data String only String only
Async ✔ Yes ✘ No ✘ No
Indexes ✔ Yes ✘ No ✘ No
Sent to Server ✘ No ✘ No ✔ Yes
Best Use Offline app data Simple preferences Sessions and authentication

IndexedDB Best Practices

  • Use IndexedDB for large structured client-side data.
  • Create object stores and indexes only inside onupgradeneeded.
  • Use readonly transactions when only reading data.
  • Use readwrite transactions only when modifying data.
  • Keep records small and avoid unnecessary duplication.
  • Handle all request errors with onerror.
  • Version your database carefully when changing schema.
  • Do not store sensitive data without encryption and a clear security plan.

Common IndexedDB Mistakes

  • Trying to use IndexedDB like synchronous Local Storage.
  • Creating object stores outside onupgradeneeded.
  • Forgetting to increase the database version after schema changes.
  • Ignoring request and transaction errors.
  • Using IndexedDB for tiny values that Local Storage can handle.
  • Storing sensitive data without encryption.
  • Using too many object stores without planning the schema.
  • Not providing offline sync conflict handling.

Key Takeaways

  • IndexedDB is a client-side database for large structured data.
  • Use indexedDB.open() to create or open a database.
  • Use object stores to save records.
  • Use transactions for reading and writing data safely.
  • Use indexes for efficient search by specific fields.
  • IndexedDB is ideal for offline-first and Progressive Web Apps.

Pro Tip

IndexedDB is powerful but verbose. In production applications, many teams use helper libraries such as Dexie.js or idb to simplify database operations while still using IndexedDB under the hood.