Manage Scopes and Collections
Scopes and collections allow you to organize your documents within a database.
Browser-Specific Behavior
| Due to IndexedDB requirements, collections and their indexes must be declared when opening the database. Collections cannot be created or deleted while the database is open. This is different from native Couchbase Lite SDKs. |
To modify collections, you must:
-
Close the database
-
Reopen it with the new collection configuration
Default Scopes and Collections
Every database you create contains a default scope and a default collection named _default.
If you create a document in the database and don’t specify a specific scope or collection, it is saved in the default collection, in the default scope.
The default scope and collection cannot be dropped.
Scope and Collection Naming
In Couchbase Lite JavaScript, scopes and collections are specified using a combined notation:
| Format |
|
| Examples |
|
Naming conventions:
-
Must be between 1 and 251 characters in length
-
Can only contain the characters
A-Z,a-z,0-9, and the symbols_,-, and% -
Cannot start with
_or%(except for the reserved_defaultscope and collection) -
Scope and collection names are case sensitive
Declare Collections
Collections must be declared in the DatabaseConfig when opening the database.
interface TravelAppSchema {
'inventory.airline': Ariline;
'inventory.hotel': Hotel;
}
const travelConfig: DatabaseConfig<TravelAppSchema> = {
name: 'travel',
version: 1,
collections: {
'inventory.airline': {
indexes: ['name', 'icao']
},
'inventory.hotel': {
indexes: ['country', 'city']
}
}
};
const travelDatabase = await Database.open(travelConfig);
| 1 | Database configuration |
| 2 | Collections with custom scope using dot notation |
| 3 | Indexes for each collection |
| 4 | Open database with configuration |
Access Collections
Once declared, you access collections through the database.collection object:
const tasks = database.collections.tasks;
const users = database.collections.users;
const inventoryAirlines = travelDatabase.collections['inventory.airline'];
| 1 | Access collections in default scope using dot notation |
| 2 | Access collection in custom scope using bracket notation |
When accessing collections with custom scopes, use bracket notation with the full "scope.collection" string: database.collection['scope.collection']
|
Collection Configuration
Each collection can have its own configuration when declared:
const database = await Database.open('secure-app', {
password: 'encryption-password',
collections: {
// Collection with default configuration
tasks: {},
// Collection with indexes (indexed properties are not encrypted)
users: {
indexes: ['username', 'email', 'role']
},
// Collection in custom scope with configuration
'private.documents': {
indexes: ['type', 'category', 'createdAt']
}
}
});
// Access configured collections
const users = database.collection.users;
const privateDocuments = database.collection['private.documents'];
| 1 | Default configuration (all properties encrypted if database has password) |
| 2 | Specify properties to leave unencrypted (can be indexed) |
| 3 | Custom scope collection with configuration |
Add Collections
To add new collections, you must close and reopen the database with the updated configuration:
await database.close();
const updatedConfig: DatabaseConfig<AppSchema> = {
name: 'myapp',
version: 2, // Increment version
collections: {
tasks: {},
users: {},
projects: {} // New collection
}
};
const updatedDb = await Database.open(updatedConfig);
| 1 | Close the database |
| 2 | Increment version number |
| 3 | Add new collection to configuration |
| 4 | Reopen database with new collection |
| All existing collections must be included when reopening. Omitting a collection from the configuration will make it inaccessible (though its data remains in IndexedDB). |
Remove Collections
To remove a collection, close the database and reopen without that collection in the configuration:
// Database with three collections
const database = await Database.open('myapp', {
collections: {
tasks: {},
users: {},
archived: {}
}
});
// Close the database
await database.close();
// Reopen without the 'archived' collection
const updatedDatabase = await Database.open('myapp', {
collections: {
tasks: {},
users: {}
// 'archived' collection omitted
}
});
// The 'archived' collection is no longer accessible
console.log('Collection removed from configuration');
| 1 | Original collections including 'archived' |
| 2 | Reopen without 'archived' |
| 3 | Collection no longer accessible |
| Removing a collection from the configuration does not delete its data from IndexedDB. The data remains but is inaccessible. To permanently delete the collection’s data, you must delete the documents before removing the collection from the configuration. |
Purge Collection Data
To permanently delete a collection’s data:
| Purging deletes all traces of a document, without leaving a "tombstone" revision behind. However, this means purges are not visible to the replicator, which has two side effects: |
-
A push replication will not push the deletion to a server.
-
If the document is later updated on the server side, the next pull replication will download the new revision.
const database = await Database.open('myapp', {
collections: {
tasks: {},
archived: {}
}
});
// Get all documents in the collection
const archived = database.collection.archived;
const query = database.createQuery('SELECT META().id FROM archived');
const results = await query.execute();
// Purge all documents
for (const row of results) {
const docId = row.id;
await archived.purge(docId);
}
console.log('All documents purged from archived collection');
// Now close and reopen without the collection
await database.close();
const updatedDatabase = await Database.open('myapp', {
collections: {
tasks: {}
// 'archived' removed after purging its data
}
});
| 1 | Query all document IDs in the collection |
| 2 | Purge each document |
| 3 | Reopen without the collection |
Index a Collection
Indexes must be declared in the DatabaseConfig when opening the database, similar to collections:
const config: DatabaseConfig = {
name: 'myapp',
version: 1,
collections: {
tasks: {
indexes: ['title', 'completed', 'createdAt']
}
}
};
const database = await Database.open(config);
| 1 | Define indexes in collection configuration |
| 2 | Properties to index |
See Indexing for comprehensive information on creating and using indexes.
List Scopes and Collections
You can retrieve a list of all scopes and their collections:
const collectionNames = database.collectionNames;
for (const name of collectionNames) {
console.log('Collection:', name);
}
| 1 | Get all collection names from database |
Get a Specific Collection
To get a collection by its full name:
// Get collection from default scope
const tasks = database.collection.tasks;
// Get collection from custom scope
const inventoryAirlines = database.collection['inventory.airlines'];
// Check if collection exists
if (database.collection['archive.old']) {
console.log('Collection exists');
} else {
console.log('Collection not found');
}
Collection Metadata
You can access metadata about a collection:
const tasks = database.collections.tasks;
const users = database.collections.users;
const docCount = await tasks.count();
console.log('Document count:', docCount);
Using TypeScript Schemas
TypeScript users can define type-safe schemas for scopes and collections:
interface Task {
type: 'task';
title: string;
completed: boolean;
priority: number;
createdAt: string;
}
// Note: Import as { Blob as CBLBlob } from ‘@couchbase/lite-js’
// to avoid conflict with the standard Blob type.
interface User {
type: 'user';
username: string;
email: string;
role: string;
avatar: CBLBlob | null;
}
// Define database schema
interface AppSchema {
tasks: Task;
users: User;
}
Troubleshooting
Collection Not Accessible
Problem: Collection exists but cannot be accessed
Solutions:
-
Verify collection was declared in
DatabaseConfig -
Check for typos in collection name
-
Use correct notation for custom scopes (
'scope.collection') -
Use bracket notation for custom scope collections