PouchDB provides comprehensive real-time change monitoring with filtering, live updates, and extensive event handling for both database changes and replication events. The changes feed allows applications to react to database modifications in real-time.
Creates a changes feed to monitor database modifications in real-time.
/**
* Listen to database changes
* @param options - Changes feed configuration options
* @returns Changes object with event emitter interface
*/
db.changes(options);Usage Examples:
// Basic changes feed
const changes = db.changes();
changes.on('change', (change) => {
console.log('Document changed:', change.id);
});
// Live changes feed
const liveChanges = db.changes({
live: true,
since: 'now',
include_docs: true
});
liveChanges.on('change', (change) => {
console.log('Live change:', change.doc);
});
// Filtered changes
const filteredChanges = db.changes({
live: true,
filter: (doc) => doc.type === 'user',
include_docs: true
});Fired when a document is modified in the database.
/**
* Change event fired for each document modification
* @param change - Change information object
*/
changes.on('change', (change) => {
// change.id: document ID that changed
// change.seq: sequence number of the change
// change.changes: array of change objects with rev information
// change.doc: full document (if include_docs: true)
// change.deleted: true if document was deleted
});Change Object Structure:
// Example change object
{
"id": "user_001",
"seq": 123,
"changes": [
{
"rev": "2-abc123def456"
}
],
"doc": {
"_id": "user_001",
"_rev": "2-abc123def456",
"name": "Alice Johnson",
"email": "alice.johnson@example.com"
}
}Fired when the changes feed has processed all changes and is complete.
/**
* Complete event fired when changes feed finishes
* @param info - Completion information
*/
changes.on('complete', (info) => {
// info.results: array of all changes
// info.last_seq: final sequence number
// info.status: completion status
});Fired when the changes feed encounters an error.
/**
* Error event fired when changes feed encounters an error
* @param err - Error object
*/
changes.on('error', (err) => {
// err.status: HTTP status code (if applicable)
// err.name: error name
// err.message: error message
});/**
* Cancel an active changes feed
*/
changes.cancel();Usage Example:
const changes = db.changes({ live: true });
// Cancel after 30 seconds
setTimeout(() => {
changes.cancel();
console.log('Changes feed canceled');
}, 30000);All PouchDB instances inherit from EventEmitter and support standard event handling methods.
Adds an event listener for the specified event.
/**
* Add event listener
* @param event - Event name to listen for
* @param handler - Event handler function
*/
db.on(event, handler);Adds a one-time event listener that removes itself after being called once.
/**
* Add one-time event listener
* @param event - Event name to listen for
* @param handler - Event handler function
*/
db.once(event, handler);Removes a specific event listener.
/**
* Remove specific event listener
* @param event - Event name
* @param handler - Event handler function to remove
*/
db.removeListener(event, handler);Removes all event listeners for a specific event or all events.
/**
* Remove all event listeners
* @param event - Optional event name (removes all if not specified)
*/
db.removeAllListeners(event);Fired when a database is created.
/**
* Database created event
* @param dbName - Name of the created database
*/
PouchDB.on('created', (dbName) => {
console.log(`Database created: ${dbName}`);
});Fired when a database is destroyed.
/**
* Database destroyed event
* @param dbName - Name of the destroyed database
*/
PouchDB.on('destroyed', (dbName) => {
console.log(`Database destroyed: ${dbName}`);
});Fired for debug information when debug mode is enabled.
/**
* Debug event fired for debug information
* @param info - Debug information array
*/
PouchDB.on('debug', (info) => {
console.log('Debug:', info);
});interface ChangesOptions {
/** Enable live/continuous changes feed */
live?: boolean;
/** Start sequence number or 'now' for current */
since?: number | string;
/** Include full document content in changes */
include_docs?: boolean;
/** Include conflicts array in changes */
conflicts?: boolean;
/** Include attachment information */
attachments?: boolean;
/** Return attachments as binary data */
binary?: boolean;
/** Reverse the order of changes */
descending?: boolean;
/** Maximum number of changes to return */
limit?: number;
/** Heartbeat interval for live changes (milliseconds) */
heartbeat?: number;
/** Timeout for changes request (milliseconds) */
timeout?: number;
/** Filter function or design document filter name */
filter?: string | ((doc: any, req: any) => boolean);
/** Specific document IDs to monitor */
doc_ids?: string[];
/** View name for filtered changes */
view?: string;
/** Parameters to pass to filter functions */
query_params?: { [key: string]: any };
/** Include document deletion changes */
include_deleted?: boolean;
/** Style of change results */
style?: 'main_only' | 'all_docs';
}// Monitor only user documents that are active
const userChanges = db.changes({
live: true,
include_docs: true,
filter: (doc) => {
return doc.type === 'user' && doc.active === true;
}
});
userChanges.on('change', (change) => {
console.log('Active user changed:', change.doc.name);
});// First, create a design document with a filter function
await db.put({
_id: '_design/filters',
filters: {
active_users: function(doc, req) {
return doc.type === 'user' && doc.active;
}.toString()
}
});
// Use the design document filter
const changes = db.changes({
live: true,
filter: 'filters/active_users',
include_docs: true
});// Monitor changes to specific documents only
const specificChanges = db.changes({
live: true,
doc_ids: ['user_001', 'user_002', 'user_003'],
include_docs: true
});
specificChanges.on('change', (change) => {
console.log(`Monitored document ${change.id} changed`);
});const changes = db.changes({
live: true,
since: 'now',
include_docs: true,
heartbeat: 10000
});
changes.on('change', (change) => {
try {
// Process the change
processDocumentChange(change.doc);
} catch (err) {
console.error('Error processing change:', err);
}
});
changes.on('error', (err) => {
console.error('Changes feed error:', err);
// Restart changes feed after a delay
setTimeout(() => {
console.log('Restarting changes feed...');
startChangesMonitoring();
}, 5000);
});
changes.on('complete', (info) => {
console.log('Changes feed completed:', info.last_seq);
});
// Function to process document changes
function processDocumentChange(doc) {
if (doc.type === 'user') {
updateUserInterface(doc);
} else if (doc.type === 'message') {
displayNewMessage(doc);
}
}let changeBuffer = [];
const BATCH_SIZE = 10;
const BATCH_TIMEOUT = 5000;
const changes = db.changes({
live: true,
include_docs: true
});
changes.on('change', (change) => {
changeBuffer.push(change);
if (changeBuffer.length >= BATCH_SIZE) {
processBatch();
}
});
// Process batches periodically
setInterval(() => {
if (changeBuffer.length > 0) {
processBatch();
}
}, BATCH_TIMEOUT);
function processBatch() {
const batch = changeBuffer.splice(0);
console.log(`Processing batch of ${batch.length} changes`);
// Process all changes in the batch
batch.forEach((change) => {
// Process individual change
console.log(`Processing change for document: ${change.id}`);
});
}// Store last processed sequence
let lastSeq = localStorage.getItem('lastProcessedSeq') || 0;
function startChangesMonitoring() {
const changes = db.changes({
live: true,
since: lastSeq,
include_docs: true
});
changes.on('change', (change) => {
// Process the change
processChange(change);
// Update and store last processed sequence
lastSeq = change.seq;
localStorage.setItem('lastProcessedSeq', lastSeq);
});
changes.on('error', (err) => {
console.error('Changes error:', err);
// Restart after delay
setTimeout(startChangesMonitoring, 5000);
});
return changes;
}
// Start monitoring
const changes = startChangesMonitoring();// High-frequency changes with batching
const changes = db.changes({
live: true,
heartbeat: 30000, // Longer heartbeat interval
timeout: 60000, // Longer timeout
limit: 100 // Process in batches
});
// Memory-efficient changes monitoring
const changes = db.changes({
live: true,
include_docs: false, // Don't include full docs if not needed
since: 'now' // Start from current position
});
// When full documents are needed, fetch them separately
changes.on('change', async (change) => {
if (shouldProcessDocument(change)) {
try {
const doc = await db.get(change.id);
processDocument(doc);
} catch (err) {
console.error(`Error fetching document ${change.id}:`, err);
}
}
});