or run

npx @tessl/cli init
Log in

Version

Files

docs

attachments.mdbulk-queries.mdchanges-events.mddatabase-operations.mdindex.mdreplication-sync.md
tile.json

changes-events.mddocs/

Changes & Events

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.

Capabilities

Changes Feed

db.changes()

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
});

Changes Events

change Event

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"
  }
}

complete Event

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
});

error Event

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
});

Changes Control

Canceling Changes Feed

/**
 * 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);

Event Emitter Interface

All PouchDB instances inherit from EventEmitter and support standard event handling methods.

Event Methods

db.on()

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);

db.once()

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);

db.removeListener()

Removes a specific event listener.

/**
 * Remove specific event listener
 * @param event - Event name
 * @param handler - Event handler function to remove
 */
db.removeListener(event, handler);

db.removeAllListeners()

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);

Database Events

created 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}`);
});

destroyed Event

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}`);
});

debug Event

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);
});

Configuration Options

Changes Options

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';
}

Advanced Usage Examples

Filtered Changes with Custom Function

// 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);
});

Changes with Design Document Filter

// 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
});

Monitoring Specific Documents

// 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`);
});

Changes Feed with Comprehensive Error Handling

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);
  }
}

Batch Processing Changes

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}`);
  });
}

Changes Feed with Resumption

// 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();

Performance Considerations

Optimizing Changes Feed Performance

// 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);
    }
  }
});