Databases
Description — Working with Couchbase Lite Databases in JavaScript
Related Content — Encryption | Blobs | Documents | Indexing
Database Concepts
Databases created on Couchbase Lite can share the same hierarchical structure as Capella databases. This makes it easier to sync data between browser applications and applications built using Capella.
Although the terminology is different, the structure can be mapped to relational database terms:
| Relational database | Couchbase |
|---|---|
Database |
Database |
Schema |
Scope |
Table |
Collection |
This structure gives you plenty of choices when it comes to partitioning your data. The most basic structure is to use the single default scope with a single default collection; or you could opt for a structure that allows you to split your collections into logical scopes.
Browser Storage
Couchbase Lite JavaScript stores data in the browser’s IndexedDB, a robust client-side storage API.
-
Stores data persistently across browser sessions
-
Subject to browser storage quotas
-
Data is scoped per origin (protocol + domain + port)
-
Supports concurrent access from multiple browser tabs
-
May be evicted under storage pressure on some browsers
Create or Open Database
You can create a new database and-or open an existing database, using the Database class. Pass in a database name and a DatabaseConfig — see Example 1.
Things to watch for include:
-
If the named database does not exist, a new one is created
-
The database is stored in the browser’s IndexedDB under the current origin
Unlike native Couchbase Lite SDKs, collections and their indexes must be declared in the DatabaseConfig when opening the database. Collections cannot be created or deleted while the database is open.
|
const config: DatabaseConfig<AppSchema> = {
name: 'myapp',
version: 1,
collections: {
tasks: {},
users: {}
}
};
const database = await Database.open(config);
console.log('Database opened:', database.name);
| 1 | Database configuration with name and version |
| 2 | Collections configuration (required) |
| 3 | Open the database |
Database Configuration
The DatabaseConfig interface provides options for configuring your database:
| Property | Type | Description |
|---|---|---|
|
CollectionsConfig |
Declares all collections and their configurations. Required. |
|
string |
Encryption password for database encryption at rest. See Database Encryption. Optional. |
Each collection can have its own configuration within the collections object:
| Property | Type | Description |
|---|---|---|
|
string[] |
Index properties for the collection. Indexed properties are not encrypted when database encryption is enabled. See Indexing and Database Encryption. Optional. |
const encryptedConfig: DatabaseConfig = {
name: 'secure-app', (1)
version: 1,
password: 'my-secure-password', (2)
collections: {
users: {
indexes: ['username', 'email'] (3)
}
}
};
const secureDb = await Database.open(encryptedConfig);
| 1 | Database name and version |
| 2 | Encryption password |
| 3 | Unencrypted properties (can be indexed) |
Close Database
You are advised to incorporate the closing of all open databases into your application workflow.
To close a database, use Database.close() — see: Example 3. This also closes active replications, listeners and-or live queries connected to the database.
| Closing a database soon after starting a replication involving it can cause an exception as the asynchronous replicator may not yet be connected. |
|
Safely Closing a Database
Before closing, check that any attached listeners (query/replication/change) indicate they are at least at connected status before closing — see for example: Monitor Status.
|
await database.close();
console.log('Database closed');
Reopen Database
After closing a database, you can reopen it using the same configuration. If the database was encrypted, you must provide the password when reopening the database — see Database Encryption for more information.
Database Encryption
Couchbase Lite JavaScript supports encrypting databases stored in IndexedDB to secure data at rest.
For complete information on database encryption, including how to enable encryption, manage encryption keys, and understand encryption limitations in browser environments, see Database Encryption.
Delete Database
To permanently delete a database and all its data from IndexedDB:
// Close the database first
await database.close();
// Delete the database
await Database.delete('myapp');
console.log('Database deleted');
| Deleting a database is permanent and cannot be undone. All data, collections, and indexes are removed from IndexedDB. |
Multiple Databases
An application can open and use multiple databases simultaneously. Each database is stored separately in IndexedDB.
// Open multiple databases
const userDb = await Database.open('users', {
collections: { profiles: {} }
});
const contentDb = await Database.open('content', {
collections: { articles: {}, comments: {} }
});
const localDb = await Database.open('local-config', {
collections: { settings: {} }
});
// Use them independently
await userDb.collection.profiles.save({...});
await contentDb.collection.articles.save({...});
// Close when done
await userDb.close();
await contentDb.close();
await localDb.close();
Storage Management
Databases in Couchbase Lite JavaScript are subject to browser storage quotas and policies.
IndexedDB quotas, persistence, and eviction rules vary by browser, so the same app may store different amounts of data across environments. When writes exceed quota, a QuotaExceededError is thrown with no automatic retry or cleanup. The application must catch these errors, decide how to free space, or prompt the user. The SDK relies entirely on IndexedDB’s native behavior. See Browsers and Storage for detailed information.
|
Storage Quotas
Browsers enforce storage quotas that vary by platform:
-
Desktop browsers: Typically 10% of available disk space
-
Mobile browsers: Typically 50-100 MB
-
Private browsing: Limited or no persistent storage
For detailed storage limits by browser, see Browser-Specific Considerations.
The SDK provides APIs to check available storage:
if (navigator.storage && navigator.storage.estimate) {
const estimate = await navigator.storage.estimate();
console.log('Quota:', estimate.quota);
console.log('Usage:', estimate.usage);
console.log('Available:', estimate.quota - estimate.usage);
const percentUsed = (estimate.usage / estimate.quota) * 100;
console.log(`Storage: ${percentUsed.toFixed(2)}% used`);
}
Request Persistent Storage
To reduce the risk of storage eviction, request persistent storage:
if (navigator.storage && navigator.storage.persist) {
const isPersistent = await navigator.storage.persist();
if (isPersistent) {
console.log('Persistent storage granted');
} else {
console.log('Persistent storage not granted');
}
}
Database Maintenance
From time to time it may be necessary to perform maintenance on your database, such as compacting the database to remove unused documents and blobs.
Couchbase Lite’s API provides the Database.performMaintenance() method.
The available maintenance operations include compact, reindex, and integrityCheck.
This is a resource-intensive operation and is not performed automatically. It should be run on-demand using the API.
// Compact the database to reclaim space
await database.performMaintenance('compact');
console.log('Database compacted');
Change Listeners
IndexedDB allows concurrent reading from multiple tabs. Write operations are serialized per store/transaction, not per tab. IndexedDB does not have built-in cross-tab change notifications; these must be implemented by the SDK or the app.
The SDK provides two types of change listeners, depending on the granularity your application needs:
Collection Change Listeners
-
Trigger whenever any document in the collection changes
-
Useful for keeping UI or state synchronized broadly
const collection = database.collection.tasks;
// Fires when any document in the collection changes
const token = collection.addChangeListener((changes) => {
console.log("Changed docs:", changes.documentIDs);
refreshUI();
});
// Remove listener when done
collection.removeChangeListener(token);
Document Change Listeners
-
Trigger only when a specific document changes
-
Useful when you want focused updates without scanning the whole collection
const collection = database.collection.tasks;
const docId = "task_001";
// Fires only when the given document changes
const token = collection.addDocumentChangeListener(docId, (change) => {
console.log("Document updated:", change.documentID);
updateTaskView(docId);
});
// Remove listener when done
collection.removeDocumentChangeListener(token);
Troubleshooting
You should use browser console logs as your first source of diagnostic information. If the information in the default logging level is insufficient, you can focus it on database errors and generate more verbose messages — see: Example 11.
For more on using Couchbase logs — see: Using Logs.
import { configure, getConsoleSink } from '@logtape/logtape';
import { LogCategory } from '@couchbase/lite-js';
// Configure logging for database operations
await configure({
sinks: {
console: getConsoleSink(),
},
loggers: [
{
category: [LogCategory, 'DB'],
lowestLevel: 'debug',
sinks: ['console'],
}
],
});
Common Issues
QuotaExceededError
Problem: Database operations fail with QuotaExceededError
Solutions:
-
Request persistent storage
-
Reduce data size
-
Compact the database
-
Delete unnecessary documents
See troubleshooting-storage.adoc for more solutions.