PouchDB Redux Middleware

raw JSON →
1.2.0 verified Thu Apr 23 auth: no javascript abandoned

pouch-redux-middleware is a Redux middleware designed to synchronize a specified segment of the Redux store's state with a PouchDB database. It enables bi-directional data flow: changes in the PouchDB database (e.g., via replication or direct writes) are automatically propagated to the Redux store as dispatched actions, and conversely, modifications to the synchronized part of the Redux state are persisted back into PouchDB. The current stable version is 1.2.0. This package's primary differentiator is its abstraction of PouchDB's change feed into Redux actions, allowing developers to manage PouchDB data through standard Redux patterns. However, its development has ceased, meaning it is not actively maintained and may have compatibility issues with modern JavaScript ecosystems.

error TypeError: PouchDB is not a constructor
cause PouchDB was not imported correctly or is not available in the current environment/scope.
fix
Ensure import PouchDB from 'pouchdb'; or const PouchDB = require('pouchdb'); is at the top of your file and that pouchdb is installed (npm install pouchdb).
error Error: Middleware argument must be a function
cause `PouchMiddleware` was not imported as a function, likely due to incorrect CommonJS `require` syntax without `.default` or a named import when a default was expected.
fix
Correct the import statement to import PouchMiddleware from 'pouch-redux-middleware'; for ESM or const PouchMiddleware = require('pouch-redux-middleware').default; for CommonJS.
error ReferenceError: types is not defined
cause The `types` object (or your `ActionTypes` constant) holding your action type strings is not imported or defined in the scope where `pouchMiddleware` is configured.
fix
Ensure you have import * as types from './constants/ActionTypes' (or similar) at the top of your configuration file, pointing to a valid file defining your action type constants.
error Redux state path is not syncing with PouchDB changes, or vice-versa.
cause Incorrect `path` configuration, `changeFilter` too restrictive, or issues with the `actions` mapping causing events to be ignored or improperly dispatched.
fix
Verify that the path option in PouchMiddleware matches the path in your Redux state tree. Debug the changeFilter function (if used) to ensure it's not filtering out desired documents. Check the actions object to ensure it returns valid Redux actions.
breaking The `pouch-redux-middleware` package has been abandoned since 2017 with no further updates or maintenance. This means it is unlikely to be compatible with modern versions of Redux (e.g., Redux Toolkit), React, Node.js, or browser environments, leading to potential runtime errors or unexpected behavior.
fix Consider using alternative, actively maintained solutions for syncing data with Redux, or implement custom middleware for PouchDB integration.
gotcha Due to its abandoned status, `pouch-redux-middleware` relies on older versions of its core dependencies, Redux and PouchDB. Using it with modern versions of these libraries (e.g., Redux 4+, PouchDB 7+) may lead to API incompatibilities or unexpected runtime issues.
fix Check the specific Redux and PouchDB versions that were active around 2017 (e.g., Redux 3.x, PouchDB 6.x) and attempt to match those if absolute necessity requires using this middleware, though this is not recommended for new projects.
gotcha Bi-directional synchronization, especially when modifying state based on database changes and vice-versa, can introduce complex race conditions or lead to infinite loops if action dispatching and database updates are not carefully managed. Ensure your action creators prevent redundant dispatches or updates.
fix Implement robust action throttling, debouncing, or idempotent update logic within your Redux reducers and PouchDB action handlers to prevent unintended recursive updates.
gotcha Performance may degrade with very large PouchDB databases or high frequencies of changes. The middleware dispatches Redux actions for every database change, which can impact application responsiveness if not optimized.
fix Utilize the `changeFilter` option to limit which documents trigger Redux updates, or consider batching Redux actions if your application can tolerate slight delays in UI updates.
gotcha The middleware's path-based configuration might not align well with modern Redux state management patterns, particularly those favoring normalized state or using libraries like Redux Toolkit's `createSlice` for structured reducers.
fix If integrating with modern Redux, you may need to write additional selectors or normalizers to transform the data structure provided by `pouch-redux-middleware` into a format suitable for your application's state.
npm install pouch-redux-middleware
yarn add pouch-redux-middleware
pnpm add pouch-redux-middleware

This quickstart demonstrates how to configure a Redux store with `pouch-redux-middleware`, connecting a specific state path (`/todos`) to a PouchDB instance. It shows how to map PouchDB changes to Redux actions for insert, update, and remove events, and includes basic PouchDB interaction to illustrate bi-directional sync.

import * as types from './constants/ActionTypes';
import PouchMiddleware from 'pouch-redux-middleware';
import { createStore, applyMiddleware } from 'redux';
import rootReducer from './reducers'; // Assume this file defines your root reducer
import PouchDB from 'pouchdb';

// Placeholder for a simple reducer if rootReducer doesn't exist for quick testing
// If you don't have a 'reducers' file, create a dummy one or replace 'rootReducer'
// with a simple function like (state = { todos: [] }, action) => state;
const dummyReducer = (state = { todos: [] }, action) => {
  switch (action.type) {
    case types.INSERT_TODO:
      return { ...state, todos: [...state.todos, action.todo] };
    case types.UPDATE_TODO:
      return { ...state, todos: state.todos.map(t => (t._id === action.todo._id ? action.todo : t)) };
    case types.DELETE_TODO:
      return { ...state, todos: state.todos.filter(t => t._id !== action.id) };
    case types.BATCH_INSERT_TODOS:
      return { ...state, todos: [...state.todos, ...action.todos] };
    default:
      return state;
  }
};

// Placeholder for ActionTypes if not defined
const dummyActionTypes = {
  DELETE_TODO: 'DELETE_TODO',
  INSERT_TODO: 'INSERT_TODO',
  BATCH_INSERT_TODOS: 'BATCH_INSERT_TODOS',
  UPDATE_TODO: 'UPDATE_TODO'
};

export default function configureStore() {
  // Using in-memory PouchDB for simple demonstration. For persistence, use a file path or URL.
  const db = new PouchDB('todos-db-' + Math.random()); 

  const pouchMiddleware = PouchMiddleware({
    path: '/todos', // Path in Redux state where todos will be stored
    db,
    actions: {
      remove: doc => { return { type: (types.DELETE_TODO || dummyActionTypes.DELETE_TODO), id: doc._id } },
      insert: doc => { return { type: (types.INSERT_TODO || dummyActionTypes.INSERT_TODO), todo: doc } },
      batchInsert: docs => { return { type: (types.BATCH_INSERT_TODOS || dummyActionTypes.BATCH_INSERT_TODOS), todos: docs } },
      update: doc => { return { type: (types.UPDATE_TODO || dummyActionTypes.UPDATE_TODO), todo: doc } }
    },
    // Optional: changeFilter to only sync specific document types
    // changeFilter: (doc) => doc.type === 'todo'
  });

  const store = createStore(
    rootReducer || dummyReducer, // Use actual rootReducer or dummy one
    undefined,
    applyMiddleware(pouchMiddleware)
  );

  // Example: Manually add a todo to PouchDB, it should sync to Redux
  setTimeout(() => {
    db.post({ _id: 'item-1', text: 'Buy milk', completed: false, createdAt: new Date().toISOString() })
      .then(response => console.log('Doc added to PouchDB:', response))
      .catch(err => console.error('Error adding doc to PouchDB:', err));
  }, 1000);
  
  // Example: Subscribe to Redux store changes to see the sync
  store.subscribe(() => {
    console.log('Redux state updated:', store.getState());
  });

  return store;
}