Skip to main content

Rindexed Storage Adapter

Rindexed is GUN’s storage adapter for browser environments. It stores data in IndexedDB, the browser’s native database, with automatic fallback to in-memory storage when IndexedDB is unavailable.

Installation

Rindexed is included with GUN and automatically enabled in browser environments:
<!-- Include GUN in your HTML -->
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gun/lib/rindexed.js"></script>

<script>
  const gun = Gun({
    file: 'myapp'  // IndexedDB database name
  });
</script>

With Module Bundlers

import Gun from 'gun';
import 'gun/lib/rindexed';

const gun = Gun({
  file: 'myapp-data'
});

With Node.js (for testing)

const Gun = require('gun');
require('gun/lib/rindexed');

const gun = Gun({
  file: 'test-db'
});

Configuration Options

Rindexed accepts the following configuration options:
const Store = require('gun/lib/rindexed');

const store = Store({
  file: 'myapp',           // IndexedDB database name
  indexedDB: indexedDB    // Optional: provide IndexedDB instance
});

Key Options

file (string)

Name of the IndexedDB database. This will appear in browser DevTools under Application > IndexedDB. Default: 'radata'
const gun = Gun({ file: 'myapp-v1' });

indexedDB (object)

Optional IndexedDB instance. Useful for testing or when using a custom IndexedDB implementation. Default: window.indexedDB
const gun = Gun({ 
  file: 'myapp',
  indexedDB: window.indexedDB || window.mozIndexedDB
});

How Rindexed Works

IndexedDB Storage

Rindexed creates an IndexedDB database with a single object store:
// Browser creates:
// Database: 'myapp'
// Object Store: 'myapp'
// Keys: encoded file names
// Values: JSON-encoded data

Storage Structure

Data is organized as key-value pairs:
// Example data stored in IndexedDB
{
  // Key: "user%1Balice%1Bname"
  // Value: "{\":\":\"Alice\",\">\":1234567890}"
  
  // Key: "user%1Balice%1Bage"
  // Value: "{\":\":30,\">\":1234567891}"
}

Automatic Fallback

If IndexedDB is unavailable (e.g., private browsing, file:// protocol), Rindexed automatically falls back to in-memory storage:
// Automatically detects and falls back
const gun = Gun({ file: 'myapp' });

// Check if using IndexedDB or fallback
if (gun.opt.store.d) {
  console.warn('Using in-memory fallback (data will not persist)');
} else {
  console.log('Using IndexedDB storage');
}

Connection Reset (WebKit Bug Workaround)

Rindexed automatically closes and reopens the IndexedDB connection every 15 seconds to work around a WebKit bug:
// From rindexed.js:60
setInterval(function() {
  if (db) {
    db.close();
    db = null;
    store.start();
  }
}, 1000 * 15);

Storage Interface

Rindexed provides a standard storage interface:
const store = RindexedDB({ file: 'myapp' });

// Write data
store.put('key', 'value', function(err, ok) {
  if (err) {
    console.error('Write failed:', err);
  } else {
    console.log('Write successful');
  }
});

// Read data
store.get('key', function(err, value) {
  if (err) {
    console.error('Read failed:', err);
  } else {
    console.log('Value:', value);
  }
});

Browser Compatibility

Rindexed works in all modern browsers with IndexedDB support:
BrowserVersionIndexedDB Support
Chrome24+✅ Full support
Firefox16+✅ Full support
Safari10+✅ Full support
EdgeAll versions✅ Full support
Opera15+✅ Full support
iOS Safari10+✅ Full support
Android4.4+✅ Full support

Private Browsing Mode

In private/incognito mode, some browsers restrict IndexedDB:
  • Chrome/Edge: IndexedDB works normally
  • Firefox: IndexedDB works normally
  • Safari: IndexedDB may be disabled (fallback to memory)
Rindexed automatically detects this and uses in-memory storage.

Storage Quota

Browsers limit how much data you can store in IndexedDB:

Checking Available Quota

if (navigator.storage && navigator.storage.estimate) {
  navigator.storage.estimate().then(function(quota) {
    console.log('Used:', quota.usage, 'bytes');
    console.log('Total:', quota.quota, 'bytes');
    console.log('Percentage:', (quota.usage / quota.quota * 100).toFixed(2) + '%');
    console.log('Remaining:', quota.quota - quota.usage, 'bytes');
  });
}

Requesting Persistent Storage

For critical applications, request persistent storage to prevent eviction:
if (navigator.storage && navigator.storage.persist) {
  navigator.storage.persist().then(function(persistent) {
    if (persistent) {
      console.log('Storage will not be cleared except by explicit user action');
    } else {
      console.log('Storage may be cleared under storage pressure');
    }
  });
}

Typical Quota Limits

BrowserQuota TypeLimit
ChromeTemporaryUp to 60% of total disk
FirefoxTemporaryUp to 50% of free disk
SafariTemporary~1 GB
EdgeTemporaryUp to 60% of total disk

Data Persistence

When Data Persists

IndexedDB data persists:
  • ✅ Across browser sessions
  • ✅ After browser restart
  • ✅ After system reboot
  • ✅ During offline use

When Data May Be Cleared

Browsers may clear IndexedDB data:
  • ⚠️ When disk space is low (temporary storage)
  • ⚠️ After extended periods of inactivity
  • ⚠️ When user clears browser data
  • ⚠️ In private browsing mode (after session)

Working with GUN

Basic Setup

import Gun from 'gun';
import 'gun/lib/rindexed';

const gun = Gun({
  file: 'myapp-data',
  peers: ['https://gun-server.example.com/gun']
});

// Store data (automatically saved to IndexedDB)
gun.get('settings').put({
  theme: 'dark',
  notifications: true
});

// Read data (loaded from IndexedDB)
gun.get('settings').once(function(data) {
  console.log('Settings:', data);
});

Offline-First Applications

const gun = Gun({ 
  file: 'offline-app',
  localStorage: false,  // Disable localStorage
  radisk: false         // Browser doesn't use radisk
});

// Write data while offline
gun.get('todos').get('task1').put({
  title: 'Buy milk',
  done: false,
  created: Date.now()
});

// Data is stored locally in IndexedDB
// When back online, GUN syncs with peers

Progressive Web Apps (PWA)

// service-worker.js
self.addEventListener('install', function(event) {
  // GUN data is already in IndexedDB
  // No additional caching needed
});

// app.js
import Gun from 'gun';
import 'gun/lib/rindexed';

const gun = Gun({
  file: 'pwa-app',
  peers: ['https://api.example.com/gun']
});

// Request persistent storage for PWA
if (navigator.storage && navigator.storage.persist) {
  navigator.storage.persist();
}

Performance Optimization

Batching Writes

Rindexed batches operations automatically, but you can optimize further:
// Bad: Many individual writes
for (let i = 0; i < 1000; i++) {
  gun.get('items').get(i).put({ value: i });
}

// Better: Batch related data
const items = {};
for (let i = 0; i < 1000; i++) {
  items[i] = { value: i };
}
gun.get('items').put(items);

Limiting Data Size

Keep individual values small for better performance:
// Bad: Store large objects
gun.get('user/alice').put({
  name: 'Alice',
  posts: [/* 1000 posts */],
  comments: [/* 5000 comments */]
});

// Better: Separate concerns
gun.get('user/alice').put({ name: 'Alice' });
gun.get('user/alice/posts').put(/* posts */);
gun.get('user/alice/comments').put(/* comments */);

Monitoring Performance

const store = gun.opt.store;

// Track operation timing
let startTime = performance.now();

store.put('key', 'value', function(err, ok) {
  const duration = performance.now() - startTime;
  console.log('Write took', duration, 'ms');
});

Debugging

Inspecting Data in DevTools

  1. Open browser DevTools (F12)
  2. Go to Application tab
  3. Expand IndexedDB in sidebar
  4. Find your database (e.g., “myapp”)
  5. Expand object stores to view data

Reading Raw Data

// Open IndexedDB directly
const request = indexedDB.open('myapp', 1);

request.onsuccess = function(event) {
  const db = event.target.result;
  const tx = db.transaction(['myapp'], 'readonly');
  const store = tx.objectStore('myapp');
  
  const allKeys = store.getAllKeys();
  allKeys.onsuccess = function() {
    console.log('All keys:', allKeys.result);
  };
};

Clearing Data

// Clear all data for debugging
const request = indexedDB.deleteDatabase('myapp');

request.onsuccess = function() {
  console.log('Database deleted');
  location.reload();
};

request.onerror = function() {
  console.error('Failed to delete database');
};

Troubleshooting

”Warning: No indexedDB exists to persist data to!”

This warning appears when IndexedDB is unavailable. Common causes:
  1. File protocol: Load your page via http:// or https://, not file://
  2. Private browsing: Some browsers disable IndexedDB in private mode
  3. Browser too old: Update to a modern browser
// Check if IndexedDB is available
if (!window.indexedDB) {
  console.warn('IndexedDB not supported');
}

”QuotaExceededError”

You’ve exceeded the storage quota:
store.put('key', 'value', function(err) {
  if (err && err.name === 'QuotaExceededError') {
    console.error('Storage quota exceeded');
    // Clear old data or request more storage
  }
});

Connection Errors

If you see transaction errors:
// Ensure you're not keeping transactions open
store.get('key', function(err, value) {
  if (err) {
    console.error('Transaction error:', err);
  }
  // Don't perform async operations before callback
});

Best Practices

  1. Set a meaningful database name: Use file option to name your database
  2. Request persistent storage: For important apps, request persistence
  3. Monitor quota usage: Check available storage regularly
  4. Handle offline gracefully: IndexedDB works offline
  5. Clear old data: Implement data retention policies
  6. Test in private mode: Ensure fallback works
  7. Use version numbers: Include version in database name for migrations

Next Steps

RADisk Adapter

File system storage for Node.js

S3 Adapter

Cloud storage with Amazon S3

Custom Adapters

Build your own storage adapter

Offline Apps

Build offline-first applications

Build docs developers (and LLMs) love