0
# Replication and Synchronization
1
2
Bidirectional synchronization with remote databases, including continuous replication and conflict resolution. PouchDB's replication system enables offline-first applications with seamless data synchronization.
3
4
## Capabilities
5
6
### One-Way Replication
7
8
Replicate data from a source database to a target database in one direction.
9
10
```javascript { .api }
11
/**
12
* Replicate from another database to this database
13
* @param source - Source database (URL string or PouchDB instance)
14
* @param options - Replication options
15
* @returns Replication object with event emitter interface
16
*/
17
db.replicate.from(source: string | PouchDB, options?: ReplicationOptions): Replication;
18
19
/**
20
* Replicate from this database to another database
21
* @param target - Target database (URL string or PouchDB instance)
22
* @param options - Replication options
23
* @returns Replication object with event emitter interface
24
*/
25
db.replicate.to(target: string | PouchDB, options?: ReplicationOptions): Replication;
26
27
/**
28
* Static method for one-way replication between any two databases
29
* @param source - Source database
30
* @param target - Target database
31
* @param options - Replication options
32
* @returns Replication object
33
*/
34
PouchDB.replicate(source: string | PouchDB, target: string | PouchDB, options?: ReplicationOptions): Replication;
35
```
36
37
**Usage Examples:**
38
39
```javascript
40
// Replicate from remote to local
41
const replication = db.replicate.from('http://localhost:5984/remote-db', {
42
continuous: true,
43
retry: true
44
}).on('change', function(info) {
45
console.log('Received changes:', info.docs.length);
46
}).on('complete', function(info) {
47
console.log('Replication completed');
48
}).on('error', function(err) {
49
console.error('Replication error:', err);
50
});
51
52
// Replicate from local to remote
53
db.replicate.to('http://localhost:5984/remote-db')
54
.on('complete', function(info) {
55
console.log('Upload completed:', info.docs_written);
56
});
57
58
// Static replication method
59
PouchDB.replicate(localDb, remoteDb, {
60
filter: 'myapp/active_users'
61
});
62
```
63
64
### Bidirectional Synchronization
65
66
Synchronize data in both directions between databases with automatic conflict detection.
67
68
```javascript { .api }
69
/**
70
* Bidirectional synchronization with another database
71
* @param remoteDB - Remote database to sync with
72
* @param options - Synchronization options
73
* @returns Sync object with event emitter interface
74
*/
75
db.sync(remoteDB: string | PouchDB, options?: SyncOptions): Sync;
76
77
/**
78
* Static method for bidirectional sync between any two databases
79
* @param source - First database
80
* @param target - Second database
81
* @param options - Synchronization options
82
* @returns Sync object
83
*/
84
PouchDB.sync(source: string | PouchDB, target: string | PouchDB, options?: SyncOptions): Sync;
85
86
interface SyncOptions extends ReplicationOptions {
87
// Inherits all replication options
88
}
89
90
interface Sync extends EventEmitter {
91
cancel(): void;
92
on(event: 'change', listener: (info: SyncChangeInfo) => void): Sync;
93
on(event: 'complete', listener: (info: SyncComplete) => void): Sync;
94
on(event: 'error', listener: (error: Error) => void): Sync;
95
on(event: 'active', listener: () => void): Sync;
96
on(event: 'paused', listener: () => void): Sync;
97
}
98
```
99
100
**Usage Examples:**
101
102
```javascript
103
// Basic bidirectional sync
104
const sync = db.sync('http://localhost:5984/remote-db', {
105
live: true,
106
retry: true
107
}).on('change', function(info) {
108
console.log('Sync change:', info.direction);
109
if (info.direction === 'pull') {
110
console.log('Downloaded:', info.change.docs.length);
111
} else {
112
console.log('Uploaded:', info.change.docs_written);
113
}
114
}).on('active', function() {
115
console.log('Sync active');
116
}).on('paused', function() {
117
console.log('Sync paused');
118
});
119
120
// Stop sync
121
sync.cancel();
122
```
123
124
### Replication Options
125
126
Comprehensive configuration options for customizing replication behavior.
127
128
```javascript { .api }
129
interface ReplicationOptions {
130
continuous?: boolean; // Continuous replication
131
retry?: boolean; // Retry on failure
132
filter?: string | Function; // Replication filter
133
query_params?: object; // Filter parameters
134
doc_ids?: string[]; // Replicate specific documents only
135
checkpoint?: string | boolean; // Checkpoint handling
136
batch_size?: number; // Number of docs per batch (default: 100)
137
batches_limit?: number; // Number of concurrent batches (default: 10)
138
since?: number | string; // Starting sequence number
139
timeout?: number; // Request timeout
140
heartbeat?: number; // Heartbeat interval
141
selector?: MangoSelector; // Mango query selector (if supported)
142
}
143
144
interface Replication extends EventEmitter {
145
cancel(): void;
146
on(event: 'change', listener: (info: ReplicationChangeInfo) => void): Replication;
147
on(event: 'complete', listener: (info: ReplicationComplete) => void): Replication;
148
on(event: 'error', listener: (error: Error) => void): Replication;
149
on(event: 'active', listener: () => void): Replication;
150
on(event: 'paused', listener: () => void): Replication;
151
}
152
153
interface ReplicationChangeInfo {
154
docs: Document[]; // Documents that changed
155
docs_read: number; // Number of docs read
156
docs_written: number; // Number of docs written
157
doc_write_failures: number; // Number of write failures
158
errors: Error[]; // Array of errors
159
last_seq: number | string; // Last sequence processed
160
start_time: string; // Replication start time
161
}
162
```
163
164
**Usage Examples:**
165
166
```javascript
167
// Filtered replication
168
const filteredReplication = db.replicate.from(remoteDb, {
169
filter: function(doc) {
170
return doc.type === 'user' && doc.active === true;
171
},
172
continuous: true,
173
retry: true,
174
batch_size: 50,
175
batches_limit: 5
176
});
177
178
// Replicate specific documents
179
const specificReplication = db.replicate.from(remoteDb, {
180
doc_ids: ['user1', 'user2', 'config'],
181
continuous: true
182
});
183
184
// Replicate with design document filter
185
const designDocReplication = db.replicate.from(remoteDb, {
186
filter: 'myapp/active_users',
187
query_params: {
188
department: 'engineering'
189
}
190
});
191
```
192
193
### Authentication and Security
194
195
Handle authentication for remote database replication.
196
197
```javascript
198
// Basic authentication
199
const authReplication = db.replicate.from('http://username:password@localhost:5984/remote-db');
200
201
// URL with credentials
202
const remoteUrl = 'http://localhost:5984/remote-db';
203
const replication = db.replicate.from(remoteUrl, {
204
auth: {
205
username: 'myuser',
206
password: 'mypassword'
207
}
208
});
209
210
// Custom headers
211
const headerReplication = db.replicate.from(remoteUrl, {
212
headers: {
213
'Authorization': 'Bearer ' + token,
214
'X-Custom-Header': 'value'
215
}
216
});
217
```
218
219
### Conflict Resolution
220
221
Handle and resolve conflicts that occur during replication.
222
223
```javascript
224
// Monitor for conflicts during sync
225
const conflictSync = db.sync(remoteDb, {
226
live: true,
227
retry: true
228
}).on('change', function(info) {
229
// Check for conflicts in replicated documents
230
if (info.direction === 'pull') {
231
info.change.docs.forEach(async (doc) => {
232
if (doc._conflicts) {
233
await handleConflict(doc);
234
}
235
});
236
}
237
});
238
239
async function handleConflict(doc) {
240
console.log(`Conflict in document ${doc._id}`);
241
242
// Get all conflicting revisions
243
const conflicts = await Promise.all(
244
doc._conflicts.map(rev => db.get(doc._id, { rev }))
245
);
246
247
// Resolve conflict (example: merge strategy)
248
const resolved = mergeDocuments(doc, conflicts);
249
250
// Save resolved version
251
await db.put(resolved);
252
253
// Delete conflicting revisions
254
for (const conflict of conflicts) {
255
await db.remove(conflict._id, conflict._rev);
256
}
257
}
258
259
function mergeDocuments(main, conflicts) {
260
// Implement your merge strategy
261
// This example combines all unique fields
262
const merged = { ...main };
263
264
conflicts.forEach(conflict => {
265
Object.keys(conflict).forEach(key => {
266
if (!key.startsWith('_') && !merged[key]) {
267
merged[key] = conflict[key];
268
}
269
});
270
});
271
272
return merged;
273
}
274
```
275
276
### Continuous Sync with Error Handling
277
278
Implement robust continuous synchronization with proper error handling and recovery.
279
280
```javascript
281
function startContinuousSync(localDb, remoteUrl) {
282
let syncHandler;
283
284
function createSync() {
285
syncHandler = localDb.sync(remoteUrl, {
286
live: true,
287
retry: true,
288
heartbeat: 10000,
289
timeout: 30000
290
});
291
292
syncHandler.on('change', function(info) {
293
console.log(`Sync ${info.direction}:`, info.change.docs.length, 'docs');
294
updateSyncStatus('active', info);
295
});
296
297
syncHandler.on('active', function() {
298
console.log('Sync resumed');
299
updateSyncStatus('active');
300
});
301
302
syncHandler.on('paused', function(err) {
303
if (err) {
304
console.log('Sync paused due to error:', err);
305
updateSyncStatus('error', err);
306
} else {
307
console.log('Sync paused');
308
updateSyncStatus('paused');
309
}
310
});
311
312
syncHandler.on('error', function(err) {
313
console.error('Sync error:', err);
314
updateSyncStatus('error', err);
315
316
// Restart sync after delay
317
setTimeout(() => {
318
console.log('Restarting sync...');
319
createSync();
320
}, 5000);
321
});
322
323
syncHandler.on('complete', function(info) {
324
console.log('Sync completed');
325
updateSyncStatus('complete', info);
326
});
327
328
return syncHandler;
329
}
330
331
function updateSyncStatus(status, data) {
332
// Update UI or application state
333
document.dispatchEvent(new CustomEvent('syncStatusChange', {
334
detail: { status, data }
335
}));
336
}
337
338
// Start initial sync
339
return createSync();
340
}
341
342
// Usage
343
const syncHandler = startContinuousSync(db, 'http://localhost:5984/remote-db');
344
345
// Stop sync when needed
346
function stopSync() {
347
if (syncHandler) {
348
syncHandler.cancel();
349
}
350
}
351
```
352
353
### Selective Replication
354
355
Replicate only specific types of documents or data subsets.
356
357
```javascript
358
// Replicate only user documents
359
const userReplication = db.replicate.from(remoteDb, {
360
filter: function(doc) {
361
return doc._id.startsWith('user_');
362
},
363
continuous: true
364
});
365
366
// Replicate documents modified after specific date
367
const recentReplication = db.replicate.from(remoteDb, {
368
filter: function(doc) {
369
const modifiedDate = new Date(doc.modified);
370
const cutoffDate = new Date('2023-01-01');
371
return modifiedDate > cutoffDate;
372
}
373
});
374
375
// Use query parameters with design document filter
376
const parameterizedFilter = db.replicate.from(remoteDb, {
377
filter: 'myapp/by_department',
378
query_params: {
379
department: 'sales',
380
active: true
381
}
382
});
383
```
384
385
## Event Handling Patterns
386
387
### Promise-based Replication
388
389
Convert replication to Promise for one-time operations:
390
391
```javascript
392
function replicateOnce(source, target, options = {}) {
393
return new Promise((resolve, reject) => {
394
PouchDB.replicate(source, target, {
395
...options,
396
continuous: false
397
}).on('complete', resolve).on('error', reject);
398
});
399
}
400
401
// Usage
402
try {
403
const result = await replicateOnce(localDb, remoteDb);
404
console.log('Replication completed:', result.docs_written);
405
} catch (error) {
406
console.error('Replication failed:', error);
407
}
408
```
409
410
### Sync State Management
411
412
Track synchronization state across application lifecycle:
413
414
```javascript
415
class SyncManager {
416
constructor(localDb, remoteUrl) {
417
this.localDb = localDb;
418
this.remoteUrl = remoteUrl;
419
this.syncHandler = null;
420
this.status = 'stopped';
421
this.listeners = new Set();
422
}
423
424
start() {
425
if (this.syncHandler) {
426
this.stop();
427
}
428
429
this.syncHandler = this.localDb.sync(this.remoteUrl, {
430
live: true,
431
retry: true
432
});
433
434
this.syncHandler.on('change', (info) => {
435
this.emit('change', info);
436
});
437
438
this.syncHandler.on('active', () => {
439
this.status = 'active';
440
this.emit('statusChange', 'active');
441
});
442
443
this.syncHandler.on('paused', () => {
444
this.status = 'paused';
445
this.emit('statusChange', 'paused');
446
});
447
448
this.syncHandler.on('error', (err) => {
449
this.status = 'error';
450
this.emit('error', err);
451
});
452
}
453
454
stop() {
455
if (this.syncHandler) {
456
this.syncHandler.cancel();
457
this.syncHandler = null;
458
}
459
this.status = 'stopped';
460
this.emit('statusChange', 'stopped');
461
}
462
463
on(event, callback) {
464
this.listeners.add({ event, callback });
465
}
466
467
emit(event, data) {
468
this.listeners.forEach(listener => {
469
if (listener.event === event) {
470
listener.callback(data);
471
}
472
});
473
}
474
}
475
476
// Usage
477
const syncManager = new SyncManager(db, 'http://localhost:5984/remote-db');
478
syncManager.on('statusChange', (status) => {
479
console.log('Sync status:', status);
480
});
481
syncManager.start();
482
```
483
484
## Error Handling
485
486
Handle various error scenarios in replication:
487
488
```javascript
489
const replication = db.replicate.from(remoteDb)
490
.on('error', function(err) {
491
if (err.status === 401) {
492
console.log('Authentication failed');
493
// Handle auth error
494
} else if (err.status === 404) {
495
console.log('Remote database not found');
496
// Handle missing database
497
} else if (err.name === 'timeout') {
498
console.log('Replication timeout');
499
// Retry or handle timeout
500
} else if (err.error === 'forbidden') {
501
console.log('Insufficient permissions');
502
// Handle permissions error
503
} else {
504
console.error('Replication error:', err);
505
}
506
});
507
```
508
509
Common error scenarios:
510
- Network connectivity issues
511
- Authentication and authorization failures
512
- Database not found or deleted
513
- Insufficient disk space
514
- Replication conflicts
515
- Invalid document structures