HTML IndexedDB Retrieve

Beginner
⏱️ 12 min read
📚 Updated: Jul 2026
🎯 5 Examples + 3 Try It
get() / cursor / readonly

Introduction

IndexedDB is a low-level browser API for storing large amounts of structured data on the client. After you insert records, your app needs to retrieve them—to populate lists, restore drafts, or sync offline caches when the user returns.

This tutorial focuses specifically on reading data from IndexedDB: fetching one record with get(), loading everything with getAll(), walking rows with openCursor(), and handling missing keys gracefully.

What You’ll Learn

01

get()

One by key.

02

getAll()

Every row.

03

openCursor

Iterate.

04

Indexes

Query fields.

05

readonly

Tx mode.

06

undefined

Not found.

What Is IndexedDB?

IndexedDB is a transactional, key-value database built into modern browsers. Data lives in object stores as plain JavaScript objects. The API is asynchronous—reads use requests and callbacks (or Promises if you wrap them), so the main thread stays responsive during large fetches.

Reads never block the UI like a synchronous SQL query might. That makes IndexedDB suitable for offline apps, cached catalogs, and progressive web apps that store thousands of records locally.

💡
Beginner Tip

Think of get(key) like looking up one row by id. Think of openCursor() like walking through the table one row at a time—great when you do not know every key upfront.

Setting Up IndexedDB

Before retrieving data, open the database and ensure your object store (and optional indexes) exist:

js
let db;
const request = indexedDB.open('myDatabase', 1);

request.onupgradeneeded = function (event) {
  db = event.target.result;
  const store = db.createObjectStore('myStore', { keyPath: 'id' });
  store.createIndex('name', 'name', { unique: false });
};

request.onsuccess = function (event) {
  db = event.target.result;
  /* db is ready — start readonly transactions here */
};

request.onerror = function (event) {
  console.error('Open failed:', event.target.error.message);
};

Schema setup runs only in onupgradeneeded. After that, every page load opens the existing database and you can read immediately in onsuccess.

Retrieving Data from IndexedDB

To fetch one record by its primary key, use get() inside a readonly transaction:

js
const tx = db.transaction(['myStore'], 'readonly');
const store = tx.objectStore('myStore');
const request = store.get(1); /* keyPath id = 1 */

request.onsuccess = function () {
  const data = request.result;
  if (data) {
    console.log('Data retrieved:', data);
  } else {
    console.warn('No record with id 1');
  }
};

request.onerror = function (event) {
  console.error('Get failed:', event.target.error.message);
};

For small stores where you need every record at once, modern browsers support getAll():

js
const tx = db.transaction(['myStore'], 'readonly');
const req = tx.objectStore('myStore').getAll();

req.onsuccess = function () {
  console.log('All records:', req.result); /* array of objects */
};

Using Cursors for Data Retrieval

Cursors walk through records one at a time. Use them when the dataset is large, you need to filter in JavaScript, or you want to process rows without loading all into memory:

js
const tx = db.transaction(['myStore'], 'readonly');
const store = tx.objectStore('myStore');
const request = store.openCursor();

request.onsuccess = function (event) {
  const cursor = event.target.result;
  if (cursor) {
    console.log('Key:', cursor.key, 'Value:', cursor.value);
    cursor.continue(); /* next record */
  } else {
    console.log('No more entries');
  }
};

request.onerror = function (event) {
  console.error('Cursor error:', event.target.error.message);
};

You can also open a cursor on an index to iterate records sorted by name or filtered by an indexed field:

js
const index = store.index('name');
const req = index.openCursor();

req.onsuccess = function (event) {
  const cursor = event.target.result;
  if (cursor) {
    console.log(cursor.value.name, cursor.value.email);
    cursor.continue();
  }
};

Handling Errors

Distinguish between a real error and a missing record. get() on a non-existent key succeeds with result === undefined—that is not a failure:

js
function getContact(db, id) {
  return new Promise(function (resolve, reject) {
    const tx = db.transaction(['contacts'], 'readonly');
    const req = tx.objectStore('contacts').get(id);

    req.onsuccess = function () {
      resolve(req.result); /* may be undefined */
    };
    req.onerror = function () {
      reject(req.error);
    };
    tx.onerror = function () {
      reject(tx.error);
    };
  });
}

getContact(db, 42)
  .then(function (record) {
    if (!record) {
      console.warn('Contact 42 not found');
    } else {
      console.log(record.name);
    }
  })
  .catch(function (err) {
    console.error('Read error:', err.message);
  });

Always use error.message in callbacks, not the legacy errorCode property from older tutorials.

⚡ Quick Reference

OperationCode pattern
Get one recordstore.get(key)
Get all recordsstore.getAll()
Iterate recordsstore.openCursor() + continue()
Query by indexstore.index('name').get(value)
Count recordsstore.count()
Read transactiondb.transaction(['store'], 'readonly')
One row
get(key)

By id

All rows
getAll()

Array

Walk
openCursor()

Iterate

Missing
if (result)

Check

Examples Gallery

These examples demonstrate IndexedDB read patterns. Use Try It Yourself to run live demos in the editor.

📚 Getting Started

Fetch a single record by its primary key.

Example 1 — get() by Primary Key

Load the contact with id = 1 from the contacts store.

js
const tx = db.transaction(['contacts'], 'readonly');
const getReq = tx.objectStore('contacts').get(1);

getReq.onsuccess = function () {
  const contact = getReq.result;
  if (contact) {
    console.log(contact.name, contact.email);
  } else {
    console.warn('Contact 1 not found');
  }
};
Try It Yourself

How It Works

get(1) returns the object whose keyPath matches 1, or undefined if nothing was stored with that id.

Example 2 — getAll() for Every Record

Load the entire store into an array—simple for small datasets.

js
const tx = db.transaction(['items'], 'readonly');
const req = tx.objectStore('items').getAll();

req.onsuccess = function () {
  req.result.forEach(function (item) {
    console.log(item.id, item.name);
  });
  console.log('Total:', req.result.length);
};
Try It Yourself

How It Works

getAll() is convenient but loads every row at once. For very large stores, prefer openCursor() to avoid memory spikes.

📈 Practical Patterns

Cursors, index lookups, and a complete demo page.

Example 3 — openCursor() to List All Rows

Walk the store one record at a time and build a list in the UI.

js
const items = [];
const tx = db.transaction(['items'], 'readonly');
const req = tx.objectStore('items').openCursor();

req.onsuccess = function (event) {
  const cursor = event.target.result;
  if (cursor) {
    items.push(cursor.value);
    cursor.continue();
  } else {
    console.log('Loaded', items.length, 'items');
    renderList(items);
  }
};
Try It Yourself

How It Works

Call cursor.continue() until cursor is null—that means you reached the end of the store.

Example 4 — Lookup by Index

Find a contact by email using a secondary index instead of the primary key.

js
/* index created: store.createIndex('email', 'email', { unique: true }) */

const tx = db.transaction(['contacts'], 'readonly');
const index = tx.objectStore('contacts').index('email');
const req = index.get('jane@example.com');

req.onsuccess = function () {
  if (req.result) {
    console.log('Found:', req.result.name);
  } else {
    console.warn('No contact with that email');
  }
};
Try It Yourself

How It Works

Indexes must be defined in onupgradeneeded. Then index.get(value) works like get() but searches on the indexed field.

Example 5 — Example Usage (Complete HTML Page)

A full page that retrieves data when the user clicks a button and shows JSON on screen.

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>IndexedDB Retrieve Example</title>
</head>
<body>
  <h1>IndexedDB Retrieve Example</h1>
  <button type="button" id="retrieve">Retrieve Data</button>
  <p id="result"></p>

  <script>
    let db;
    const request = indexedDB.open('myDatabase', 1);

    request.onupgradeneeded = function (event) {
      const store = event.target.result.createObjectStore('myStore', { keyPath: 'id' });
      store.createIndex('name', 'name', { unique: false });
    };

    request.onsuccess = function (event) {
      db = event.target.result;
      const seedTx = db.transaction(['myStore'], 'readwrite');
      seedTx.objectStore('myStore').put({ id: 1, name: 'Sample', value: 42 });
    };

    document.getElementById('retrieve').addEventListener('click', function () {
      const tx = db.transaction(['myStore'], 'readonly');
      const getReq = tx.objectStore('myStore').get(1);

      getReq.onsuccess = function () {
        const data = getReq.result;
        document.getElementById('result').textContent =
          data ? 'Retrieved: ' + JSON.stringify(data) : 'Not found';
      };
    });
  </script>
</body>
</html>
Try It Yourself

How It Works

The page seeds id 1 on first open. Clicking Retrieve runs get(1) in a readonly transaction and displays the JSON string—no developer console needed.

🚀 Common Use Cases

  • Restore draftsget(draftId) when the user reopens a form.
  • Populate listsgetAll() or cursors to fill todo or message lists offline.
  • Login by email — index lookup to find a cached user profile.
  • Sync status — read queued items before sending to a server.
  • Verify before deleteget() first to show what will be removed.

🧠 How IndexedDB Retrieve Works

1

readonly tx

db.transaction(['store'], 'readonly')

Open
2

get / cursor

Request the record(s) you need.

Query
3

onsuccess

Read request.result in the callback.

Result
=

Data in hand

Render in the UI or pass to your app logic.

📝 Notes

  • Use readonly transactions for all read operations.
  • get() returning undefined means “not found”—not an error.
  • getAll() loads every row; use cursors for very large stores.
  • Indexes must be created in onupgradeneeded before you can query them.
  • All requests in a transaction must complete before the transaction finishes.
  • After reading, continue to Update or Delete as needed.

Universal Browser Support

IndexedDB get(), getAll(), openCursor(), and indexes are supported in all modern browsers: Chrome, Firefox, Safari, Edge, and mobile WebViews.

Baseline · Since HTML

IndexedDB read API

IndexedDB get(), getAll(), openCursor(), and indexes are supported in all modern browsers: Chrome, Firefox, Safari, Edge, and mobile WebViews.

95% Modern browser support
Google Chrome All versions · Desktop & Mobile
Full support
Mozilla Firefox All versions · Desktop & Mobile
Full support
Apple Safari All versions · macOS & iOS
Full support
Microsoft Edge All versions · Chromium & Legacy
Full support
Internet Explorer IE 6+ · Legacy environments
Full support
Opera All modern versions
Full support
IndexedDB read API Excellent

Bottom line: Safe to use IndexedDB reads in production cross-browser web apps.

💡 Best Practices

✅ Do

  • Use readonly transactions for reads
  • Check if (result) after every get()
  • Use indexes for lookups on non-key fields
  • Prefer cursors for large datasets
  • Wrap callbacks in Promises for cleaner async code
  • Handle onerror on requests and transactions

❌ Don’t

  • Assume get() always returns an object
  • Use getAll() on huge stores without testing memory
  • Query indexes that were never created in upgrade
  • Block the UI waiting synchronously for IndexedDB
  • Use readwrite when you only need to read
  • Forget to call cursor.continue() in loops

Conclusion

Retrieving data from IndexedDB means opening a readonly transaction and using get(), getAll(), or openCursor() to read records. Check for undefined when a key is missing, and use indexes when you need to search by fields other than the primary key.

With these patterns you can build offline lists, restore user data, and verify records before updating or deleting them. Next, learn how to update existing records with put().

Key Takeaways

Knowledge Unlocked

Five things to remember about IndexedDB retrieve

Use these points when reading data from a client-side database.

5
Core concepts
📄 02

getAll()

All rows.

Batch
🔄 03

Cursor

Iterate.

Scale
🔒 04

readonly

Tx mode.

Reads
05

undefined

Not found.

Check

❓ Frequently Asked Questions

Open the database with indexedDB.open(), start a readonly transaction: db.transaction(['storeName'], 'readonly'), get the object store, then call store.get(key) for one record or store.getAll() for every record. Handle the result in the request's onsuccess callback.
get(key) returns one record by primary key instantly. openCursor() lets you iterate records one at a time—useful for large datasets, filtering in JavaScript, or walking an index without loading everything into memory at once.
When no record matches the key, get() succeeds but result is undefined. That is not an error—check if (result) before using the data and show a friendly 'not found' message in your UI.
Yes. Use db.transaction(['store'], 'readonly') for get(), getAll(), openCursor(), and count(). You can omit the mode (defaults to readonly) but being explicit is clearer for beginners.
Create an index in onupgradeneeded with createIndex(), then use store.index('indexName').get(value) or openCursor() on that index to look up records by email, name, or other fields.
Yes. get(), getAll(), openCursor(), and indexes work in Chrome, Firefox, Safari, Edge, and mobile browsers. IndexedDB is the standard API for client-side structured storage.
Did you know?

If you omit the transaction mode, IndexedDB defaults to readonly—so db.transaction(['store']) is valid for reads. Being explicit with 'readonly' makes your intent clearer to other developers reviewing the code.

Practice IndexedDB retrieve

Seed a contact, click get(1), and see the JSON object—or list every row with openCursor().

Open Try It editor →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

6 people found this page helpful