PouchDB can sync with CouchDB in reverse of what you’d expect, with CouchDB acting as the "client" and PouchDB as the "server" for specific replication events.
Let’s see it in action. Imagine a mobile app using PouchDB locally. Users add items, edit them, delete them. This data needs to go to a central CouchDB instance.
Here’s a simplified PouchDB setup in JavaScript:
const PouchDB = require('pouchdb-node');
const localDB = new PouchDB('myLocalDatabase');
const remoteDB = new PouchDB('http://admin:password@localhost:5984/myRemoteDatabase');
// Start continuous replication from local to remote
const replication = localDB.replicate.to(remoteDB, {
live: true,
retry: true
});
replication.on('change', function (info) {
// Handle replication changes, e.g., log progress
console.log('Replication change:', info);
});
replication.on('paused', function (err) {
// Replication paused, maybe due to network issues
console.log('Replication paused:', err);
});
replication.on('error', function (err) {
// Handle replication errors
console.error('Replication error:', err);
});
And here’s the CouchDB side, which doesn’t need explicit "sync" code. CouchDB just needs to be running and accessible at http://localhost:5984 with a database named myRemoteDatabase.
The magic happens when PouchDB initiates the connection. When you call localDB.replicate.to(remoteDB), PouchDB sends a request to CouchDB. CouchDB, by default, exposes a replication endpoint. PouchDB speaks the CouchDB replication protocol, asking CouchDB for changes since the last checkpoint. CouchDB responds with the relevant documents. PouchDB then writes these changes to its local store.
Conversely, to pull changes from CouchDB to PouchDB, you’d use localDB.replicate.from(remoteDB, { live: true, retry: true }). In this direction, PouchDB polls CouchDB for updates.
The core problem this solves is unreliable network connectivity. Mobile devices frequently lose or gain network access. With PouchDB, the app remains functional offline. Data is stored locally and then synchronized when a connection becomes available. It’s the bedrock of offline-first architectures.
Internally, PouchDB and CouchDB use a clever system based on document revisions and sequence numbers. When PouchDB replicates to CouchDB, it sends its local changes. CouchDB checks if it already has those revisions. If not, it accepts them. When PouchDB replicates from CouchDB, it asks CouchDB for changes since a specific sequence number. CouchDB returns all documents whose revisions have a sequence number greater than the one PouchDB provided. This ensures no data is lost and no duplicates are created.
The live: true option means the replication will continue indefinitely, automatically picking up new changes as they occur on either side. retry: true tells PouchDB to keep trying to reconnect and resynchronize if the connection drops.
A common point of confusion is how conflict resolution works. When the same document is modified on both the client (PouchDB) and the server (CouchDB) independently, a conflict arises. PouchDB doesn’t automatically resolve these. Instead, it stores both versions as "clones" under the same document ID, distinguished by their revision IDs. Your application code must then decide which version to keep. You’ll typically see documents with multiple _rev entries, and you’ll need to query for conflicts: true to find them and write a new winning revision.
The next concept you’ll likely grapple with is managing these conflicts effectively across multiple devices and the central CouchDB.