0
# Changes & Events
1
2
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.
3
4
## Capabilities
5
6
### Changes Feed
7
8
#### db.changes()
9
10
Creates a changes feed to monitor database modifications in real-time.
11
12
```javascript { .api }
13
/**
14
* Listen to database changes
15
* @param options - Changes feed configuration options
16
* @returns Changes object with event emitter interface
17
*/
18
db.changes(options);
19
```
20
21
**Usage Examples:**
22
23
```javascript
24
// Basic changes feed
25
const changes = db.changes();
26
27
changes.on('change', (change) => {
28
console.log('Document changed:', change.id);
29
});
30
31
// Live changes feed
32
const liveChanges = db.changes({
33
live: true,
34
since: 'now',
35
include_docs: true
36
});
37
38
liveChanges.on('change', (change) => {
39
console.log('Live change:', change.doc);
40
});
41
42
// Filtered changes
43
const filteredChanges = db.changes({
44
live: true,
45
filter: (doc) => doc.type === 'user',
46
include_docs: true
47
});
48
```
49
50
### Changes Events
51
52
#### change Event
53
54
Fired when a document is modified in the database.
55
56
```javascript { .api }
57
/**
58
* Change event fired for each document modification
59
* @param change - Change information object
60
*/
61
changes.on('change', (change) => {
62
// change.id: document ID that changed
63
// change.seq: sequence number of the change
64
// change.changes: array of change objects with rev information
65
// change.doc: full document (if include_docs: true)
66
// change.deleted: true if document was deleted
67
});
68
```
69
70
**Change Object Structure:**
71
72
```javascript
73
// Example change object
74
{
75
"id": "user_001",
76
"seq": 123,
77
"changes": [
78
{
79
"rev": "2-abc123def456"
80
}
81
],
82
"doc": {
83
"_id": "user_001",
84
"_rev": "2-abc123def456",
85
"name": "Alice Johnson",
86
"email": "alice.johnson@example.com"
87
}
88
}
89
```
90
91
#### complete Event
92
93
Fired when the changes feed has processed all changes and is complete.
94
95
```javascript { .api }
96
/**
97
* Complete event fired when changes feed finishes
98
* @param info - Completion information
99
*/
100
changes.on('complete', (info) => {
101
// info.results: array of all changes
102
// info.last_seq: final sequence number
103
// info.status: completion status
104
});
105
```
106
107
#### error Event
108
109
Fired when the changes feed encounters an error.
110
111
```javascript { .api }
112
/**
113
* Error event fired when changes feed encounters an error
114
* @param err - Error object
115
*/
116
changes.on('error', (err) => {
117
// err.status: HTTP status code (if applicable)
118
// err.name: error name
119
// err.message: error message
120
});
121
```
122
123
### Changes Control
124
125
#### Canceling Changes Feed
126
127
```javascript { .api }
128
/**
129
* Cancel an active changes feed
130
*/
131
changes.cancel();
132
```
133
134
**Usage Example:**
135
136
```javascript
137
const changes = db.changes({ live: true });
138
139
// Cancel after 30 seconds
140
setTimeout(() => {
141
changes.cancel();
142
console.log('Changes feed canceled');
143
}, 30000);
144
```
145
146
## Event Emitter Interface
147
148
All PouchDB instances inherit from EventEmitter and support standard event handling methods.
149
150
### Event Methods
151
152
#### db.on()
153
154
Adds an event listener for the specified event.
155
156
```javascript { .api }
157
/**
158
* Add event listener
159
* @param event - Event name to listen for
160
* @param handler - Event handler function
161
*/
162
db.on(event, handler);
163
```
164
165
#### db.once()
166
167
Adds a one-time event listener that removes itself after being called once.
168
169
```javascript { .api }
170
/**
171
* Add one-time event listener
172
* @param event - Event name to listen for
173
* @param handler - Event handler function
174
*/
175
db.once(event, handler);
176
```
177
178
#### db.removeListener()
179
180
Removes a specific event listener.
181
182
```javascript { .api }
183
/**
184
* Remove specific event listener
185
* @param event - Event name
186
* @param handler - Event handler function to remove
187
*/
188
db.removeListener(event, handler);
189
```
190
191
#### db.removeAllListeners()
192
193
Removes all event listeners for a specific event or all events.
194
195
```javascript { .api }
196
/**
197
* Remove all event listeners
198
* @param event - Optional event name (removes all if not specified)
199
*/
200
db.removeAllListeners(event);
201
```
202
203
### Database Events
204
205
#### created Event
206
207
Fired when a database is created.
208
209
```javascript { .api }
210
/**
211
* Database created event
212
* @param dbName - Name of the created database
213
*/
214
PouchDB.on('created', (dbName) => {
215
console.log(`Database created: ${dbName}`);
216
});
217
```
218
219
#### destroyed Event
220
221
Fired when a database is destroyed.
222
223
```javascript { .api }
224
/**
225
* Database destroyed event
226
* @param dbName - Name of the destroyed database
227
*/
228
PouchDB.on('destroyed', (dbName) => {
229
console.log(`Database destroyed: ${dbName}`);
230
});
231
```
232
233
#### debug Event
234
235
Fired for debug information when debug mode is enabled.
236
237
```javascript { .api }
238
/**
239
* Debug event fired for debug information
240
* @param info - Debug information array
241
*/
242
PouchDB.on('debug', (info) => {
243
console.log('Debug:', info);
244
});
245
```
246
247
## Configuration Options
248
249
### Changes Options
250
251
```javascript { .api }
252
interface ChangesOptions {
253
/** Enable live/continuous changes feed */
254
live?: boolean;
255
256
/** Start sequence number or 'now' for current */
257
since?: number | string;
258
259
/** Include full document content in changes */
260
include_docs?: boolean;
261
262
/** Include conflicts array in changes */
263
conflicts?: boolean;
264
265
/** Include attachment information */
266
attachments?: boolean;
267
268
/** Return attachments as binary data */
269
binary?: boolean;
270
271
/** Reverse the order of changes */
272
descending?: boolean;
273
274
/** Maximum number of changes to return */
275
limit?: number;
276
277
/** Heartbeat interval for live changes (milliseconds) */
278
heartbeat?: number;
279
280
/** Timeout for changes request (milliseconds) */
281
timeout?: number;
282
283
/** Filter function or design document filter name */
284
filter?: string | ((doc: any, req: any) => boolean);
285
286
/** Specific document IDs to monitor */
287
doc_ids?: string[];
288
289
/** View name for filtered changes */
290
view?: string;
291
292
/** Parameters to pass to filter functions */
293
query_params?: { [key: string]: any };
294
295
/** Include document deletion changes */
296
include_deleted?: boolean;
297
298
/** Style of change results */
299
style?: 'main_only' | 'all_docs';
300
}
301
```
302
303
## Advanced Usage Examples
304
305
### Filtered Changes with Custom Function
306
307
```javascript
308
// Monitor only user documents that are active
309
const userChanges = db.changes({
310
live: true,
311
include_docs: true,
312
filter: (doc) => {
313
return doc.type === 'user' && doc.active === true;
314
}
315
});
316
317
userChanges.on('change', (change) => {
318
console.log('Active user changed:', change.doc.name);
319
});
320
```
321
322
### Changes with Design Document Filter
323
324
```javascript
325
// First, create a design document with a filter function
326
await db.put({
327
_id: '_design/filters',
328
filters: {
329
active_users: function(doc, req) {
330
return doc.type === 'user' && doc.active;
331
}.toString()
332
}
333
});
334
335
// Use the design document filter
336
const changes = db.changes({
337
live: true,
338
filter: 'filters/active_users',
339
include_docs: true
340
});
341
```
342
343
### Monitoring Specific Documents
344
345
```javascript
346
// Monitor changes to specific documents only
347
const specificChanges = db.changes({
348
live: true,
349
doc_ids: ['user_001', 'user_002', 'user_003'],
350
include_docs: true
351
});
352
353
specificChanges.on('change', (change) => {
354
console.log(`Monitored document ${change.id} changed`);
355
});
356
```
357
358
### Changes Feed with Comprehensive Error Handling
359
360
```javascript
361
const changes = db.changes({
362
live: true,
363
since: 'now',
364
include_docs: true,
365
heartbeat: 10000
366
});
367
368
changes.on('change', (change) => {
369
try {
370
// Process the change
371
processDocumentChange(change.doc);
372
} catch (err) {
373
console.error('Error processing change:', err);
374
}
375
});
376
377
changes.on('error', (err) => {
378
console.error('Changes feed error:', err);
379
380
// Restart changes feed after a delay
381
setTimeout(() => {
382
console.log('Restarting changes feed...');
383
startChangesMonitoring();
384
}, 5000);
385
});
386
387
changes.on('complete', (info) => {
388
console.log('Changes feed completed:', info.last_seq);
389
});
390
391
// Function to process document changes
392
function processDocumentChange(doc) {
393
if (doc.type === 'user') {
394
updateUserInterface(doc);
395
} else if (doc.type === 'message') {
396
displayNewMessage(doc);
397
}
398
}
399
```
400
401
### Batch Processing Changes
402
403
```javascript
404
let changeBuffer = [];
405
const BATCH_SIZE = 10;
406
const BATCH_TIMEOUT = 5000;
407
408
const changes = db.changes({
409
live: true,
410
include_docs: true
411
});
412
413
changes.on('change', (change) => {
414
changeBuffer.push(change);
415
416
if (changeBuffer.length >= BATCH_SIZE) {
417
processBatch();
418
}
419
});
420
421
// Process batches periodically
422
setInterval(() => {
423
if (changeBuffer.length > 0) {
424
processBatch();
425
}
426
}, BATCH_TIMEOUT);
427
428
function processBatch() {
429
const batch = changeBuffer.splice(0);
430
console.log(`Processing batch of ${batch.length} changes`);
431
432
// Process all changes in the batch
433
batch.forEach((change) => {
434
// Process individual change
435
console.log(`Processing change for document: ${change.id}`);
436
});
437
}
438
```
439
440
### Changes Feed with Resumption
441
442
```javascript
443
// Store last processed sequence
444
let lastSeq = localStorage.getItem('lastProcessedSeq') || 0;
445
446
function startChangesMonitoring() {
447
const changes = db.changes({
448
live: true,
449
since: lastSeq,
450
include_docs: true
451
});
452
453
changes.on('change', (change) => {
454
// Process the change
455
processChange(change);
456
457
// Update and store last processed sequence
458
lastSeq = change.seq;
459
localStorage.setItem('lastProcessedSeq', lastSeq);
460
});
461
462
changes.on('error', (err) => {
463
console.error('Changes error:', err);
464
// Restart after delay
465
setTimeout(startChangesMonitoring, 5000);
466
});
467
468
return changes;
469
}
470
471
// Start monitoring
472
const changes = startChangesMonitoring();
473
```
474
475
## Performance Considerations
476
477
### Optimizing Changes Feed Performance
478
479
```javascript
480
// High-frequency changes with batching
481
const changes = db.changes({
482
live: true,
483
heartbeat: 30000, // Longer heartbeat interval
484
timeout: 60000, // Longer timeout
485
limit: 100 // Process in batches
486
});
487
488
// Memory-efficient changes monitoring
489
const changes = db.changes({
490
live: true,
491
include_docs: false, // Don't include full docs if not needed
492
since: 'now' // Start from current position
493
});
494
495
// When full documents are needed, fetch them separately
496
changes.on('change', async (change) => {
497
if (shouldProcessDocument(change)) {
498
try {
499
const doc = await db.get(change.id);
500
processDocument(doc);
501
} catch (err) {
502
console.error(`Error fetching document ${change.id}:`, err);
503
}
504
}
505
});
506
```