Migrating from PouchDB

      +

      Description — Couchbase Lite JavaScript — Migrating from PouchDB to Couchbase Lite
      Related Content — Databases | Replication

      Overview

      Couchbase Lite JavaScript provides an officially supported alternative to PouchDB for building offline-first web applications with Sync Gateway.

      This guide helps developers migrate from PouchDB to Couchbase Lite JavaScript.

      Why Migrate?

      Official Couchbase Support: * Full support from Couchbase * Regular updates and security patches * Enterprise-grade reliability

      Performance Improvements: * Optimized for modern browser APIs * Better sync reliability with WebSocket protocol * Improved IndexedDB performance

      Enhanced Features: * TypeScript support with full type definitions * Advanced conflict resolution strategies * Field-level encryption * Better storage quota management * Production-ready error handling

      Better Integration: * Seamless integration with Sync Gateway 3.0+ * Compatible with Couchbase Server 7.0+ * Modern authentication methods * Improved monitoring and logging

      API Comparison

      Core Operations

      Table 1. Database and Document Operations
      PouchDB API Couchbase Lite JS API Notes

      new PouchDB('mydb')

      await Database.open(config)

      Async operation

      db.info()

      database.name, database.path

      Properties, not method

      db.put(doc)

      await collection.save(doc)

      Collection-based

      db.get(id)

      await collection.document(id)

      Returns document or null

      db.remove(doc)

      await collection.deleteDocument(doc)

      Requires document object

      db.bulkDocs([…​])

      await collection.updateMultiple({save: […​]})

      Batch operations

      db.allDocs()

      collection.documents()

      Iterator pattern

      db.destroy()

      await Database.delete(name)

      Static method

      Query Operations

      Table 2. Querying Data
      PouchDB API Couchbase Lite JS API Notes

      db.find({selector})

      database.createQuery(sql)

      SQL++ instead of Mango

      db.createIndex({…​})

      Declare in DatabaseConfig

      At database open time

      db.query(mapFn)

      database.createQuery(sql)

      No map/reduce functions

      Replication

      Table 3. Sync Operations
      PouchDB API Couchbase Lite JS API Notes

      db.sync(url, opts)

      new Replicator(config)

      WebSocket-based

      db.replicate.to(url)

      replicatorType: 'push'

      In ReplicatorConfig

      db.replicate.from(url)

      replicatorType: 'pull'

      In ReplicatorConfig

      sync.on('change', fn)

      replicator.onStatusChange

      Property, not event

      sync.cancel()

      await replicator.stop()

      Async operation

      Change Events

      Table 4. Listening to Changes
      PouchDB API Couchbase Lite JS API Notes

      db.changes({live: true})

      collection.addChangeListener(fn)

      Returns token

      changes.cancel()

      collection.removeChangeListener(token)

      Use token from add

      Migration Steps

      Step 1: Install Couchbase Lite

      Install Package
      # Remove PouchDB
      npm uninstall pouchdb
      
      # Install Couchbase Lite
      npm install @couchbase/lite-js
      
      # Install LogTape for logging (optional)
      npm install @logtape/logtape

      Step 2: Update Database Initialization

      Example 1. Before (PouchDB)
      import PouchDB from 'pouchdb';
      
      const db = new PouchDB('myapp');
      Example 2. After (Couchbase Lite)
      import { Database } from '@couchbase/lite-js';
      
      const config = {
        name: 'myapp',
        version: 1,
        collections: {
          _default: {} // Default collection
        }
      };
      
      const database = await Database.open(config);
      const collection = database.collections._default;

      Step 3: Update Document Operations

      Example 3. Before (PouchDB)
      // Create document
      await db.put({
        _id: 'doc1',
        type: 'task',
        title: 'Learn Couchbase',
        completed: false
      });
      
      // Read document
      const doc = await db.get('doc1');
      
      // Update document
      doc.completed = true;
      await db.put(doc);
      
      // Delete document
      await db.remove(doc);
      Example 4. After (Couchbase Lite)
      const collection = database.collections._default;
      
      // Create document
      await collection.save({
        _id: 'doc1',
        type: 'task',
        title: 'Learn Couchbase',
        completed: false
      });
      
      // Read document
      const doc = await collection.document('doc1');
      
      // Update document
      doc.completed = true;
      await collection.save(doc);
      
      // Delete document
      await collection.deleteDocument(doc);

      Step 4: Update Queries

      Example 5. Before (PouchDB - Mango Query)
      // Create index
      await db.createIndex({
        index: {
          fields: ['type', 'completed']
        }
      });
      
      // Query documents
      const result = await db.find({
        selector: {
          type: 'task',
          completed: false
        },
        sort: ['title']
      });
      
      result.docs.forEach(doc => {
        console.log(doc.title);
      });
      Example 6. After (Couchbase Lite - SQL++)
      // Declare indexes in config (at database open)
      const config = {
        name: 'myapp',
        version: 1,
        collections: {
          _default: {
            indexes: ['type', 'completed', 'title']
          }
        }
      };
      
      const database = await Database.open(config);
      
      // Query documents with SQL++
      const query = database.createQuery(`
        SELECT *
        FROM _default
        WHERE type = 'task' AND completed = false
        ORDER BY title
      `);
      
      await query.execute(row => {
        console.log(row._default.title);
      });

      Step 5: Update Replication

      Example 7. Before (PouchDB)
      const sync = PouchDB.sync('myapp', 'http://localhost:4984/myapp', {
        live: true,
        retry: true
      });
      
      sync.on('change', info => {
        console.log('Change:', info);
      });
      
      sync.on('error', err => {
        console.error('Error:', err);
      });
      Example 8. After (Couchbase Lite)
      import { Replicator } from '@couchbase/lite-js';
      
      const replicator = new Replicator({
        database: database,
        url: 'wss://localhost:4984/myapp',
        collections: {
          _default: { pull: {}, push: {} }
        },
        credentials: {
          username: 'user',
          password: 'pass'
        },
        continuous: true
      });
      
      replicator.onStatusChange = (status) => {
        console.log('Status:', status.activity);
        if (status.error) {
          console.error('Error:', status.error);
        }
      };
      
      await replicator.start();

      Step 6: Update Change Listeners

      Example 9. Before (PouchDB)
      const changes = db.changes({
        since: 'now',
        live: true,
        include_docs: true
      });
      
      changes.on('change', change => {
        console.log('Document changed:', change.id);
      });
      
      // Cancel later
      changes.cancel();
      Example 10. After (Couchbase Lite)
      const collection = database.collections._default;
      
      const token = collection.addChangeListener((changes) => {
        changes.documentIDs.forEach(id => {
          console.log('Document changed:', id);
        });
      });
      
      // Remove listener later
      collection.removeChangeListener(token);

      Data Migration

      Export from PouchDB

      Example 11. Export All Documents
      // Export all documents from PouchDB
      const result = await pouchDB.allDocs({
        include_docs: true,
        attachments: true
      });
      
      const docs = result.rows.map(row => row.doc);
      
      // Save to file or prepare for import
      const dataExport = {
        docs: docs,
        timestamp: new Date().toISOString()
      };
      
      // Download as JSON
      const blob = new Blob(
        [JSON.stringify(dataExport, null, 2)],
        { type: 'application/json' }
      );
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = 'pouchdb-export.json';
      a.click();

      Import to Couchbase Lite

      Example 12. Import Documents
      import { Database } from '@couchbase/lite-js';
      
      // Open Couchbase Lite database
      const database = await Database.open(config);
      const collection = database.collections._default;
      
      // Load exported data
      const response = await fetch('pouchdb-export.json');
      const dataExport = await response.json();
      
      // Import documents in batches
      const batchSize = 100;
      for (let i = 0; i < dataExport.docs.length; i += batchSize) {
        const batch = dataExport.docs.slice(i, i + batchSize);
      
        const docsToSave = batch.map(doc => {
          // Remove PouchDB metadata if desired
          const { _rev, ...cleanDoc } = doc;
          return cleanDoc;
        });
      
        await collection.updateMultiple({
          save: docsToSave
        });
      
        console.log(`Imported ${Math.min(i + batchSize, dataExport.docs.length)} of ${dataExport.docs.length}`);
      }
      
      console.log('Migration complete!');

      Key Differences

      Architecture

      PouchDB: * HTTP-based replication (_changes feed, _bulk_docs) * CouchDB replication protocol * Map/reduce for queries

      Couchbase Lite: * WebSocket-based replication * Couchbase Mobile protocol * SQL++ for queries * Collection-based data organization

      Configuration

      PouchDB: * Runtime configuration * Dynamic index creation * Flexible schema

      Couchbase Lite: * Indexes declared at database open * Structured configuration * TypeScript schema support

      Authentication

      PouchDB: * Basic auth in URL or headers * Cookie-based sessions

      Couchbase Lite: * Two-step authentication (session + WebSocket) * CORS configuration required * See CORS Configuration

      Conflict Resolution

      PouchDB: * Automatic conflict resolution * Winner by revision tree depth * Limited custom resolution

      Couchbase Lite: * Automatic and custom strategies * Flexible conflict resolvers * See Handling Data Conflicts

      Common Pitfalls

      1. Sync URL Protocol

      Issue: Using HTTP URL instead of WebSocket

      Incorrect
      url: 'http://localhost:4984/myapp' // Wrong protocol
      Correct
      url: 'ws://localhost:4984/myapp'   // For development
      url: 'wss://sync.example.com/myapp' // For production (TLS)

      2. Index Creation Timing

      Issue: Trying to create indexes after database is open

      Incorrect
      const database = await Database.open(config);
      // Cannot create indexes here
      Correct
      const config = {
        name: 'myapp',
        version: 1,
        collections: {
          _default: {
            indexes: ['field1', 'field2'] // Declare indexes here
          }
        }
      };
      const database = await Database.open(config);

      3. Query Syntax

      Issue: Using Mango query syntax

      Incorrect
      // Mango queries don't work
      db.find({ selector: { type: 'task' } })
      Correct
      // Use SQL++
      database.createQuery('SELECT * FROM _default WHERE type = "task"')

      4. Change Listener Cleanup

      Issue: Not removing change listeners

      Incorrect
      // Listener never removed - memory leak
      collection.addChangeListener(fn);
      Correct
      // Store token and remove when done
      const token = collection.addChangeListener(fn);
      // Later...
      collection.removeChangeListener(token);

      How to . . .

      .

      Learn more . . .

      .

      Dive Deeper . . .

      Mobile Forum | Blog | Tutorials

      .