Redux Service Middleware

raw JSON →
22.17.0 verified Thu Apr 23 auth: no javascript

The `inst-redux-service-middleware` package provides a Redux middleware designed to standardize and simplify the process of calling external 'service' objects from within Redux actions. Instead of writing bespoke thunks or sagas for every asynchronous service interaction, this middleware allows developers to register service instances and invoke their methods via a consistent, predefined action shape. This approach reduces boilerplate and promotes a cleaner separation of concerns by centralizing service invocation logic. Currently at version 22.17.0, the package appears to be actively maintained, offering a mature solution for abstracting side effects related to service calls. Its primary differentiation lies in providing a generic mechanism for calling any registered service method, which can be particularly useful for integrating various API clients or utility functions without deep Redux-specific implementations for each.

error Error: Service 'myKey' not found in middleware configuration.
cause The dispatched action referenced a service key ('myKey') that was not registered with the `createServiceMiddleware` function when the store was created.
fix
Ensure the service key in action.payload.service exactly matches one of the keys provided to createServiceMiddleware.
error TypeError: Cannot read properties of undefined (reading 'methodName')
cause The service specified in `action.payload.service` was found, but the `method` property (`action.payload.method`) does not correspond to an existing method on that service object.
fix
Verify that the method name in your action payload is spelled correctly and exists on the registered service object.
error A state mutation was detected between dispatches
cause A service method, or a subsequent action handler, directly modified the Redux state object instead of returning a new, immutable state. This can also happen if callbacks (`onSuccess`, `onError`) or other parts of your Redux flow are not correctly handling immutability.
fix
Ensure all Redux reducers and any state transformations within service callbacks return new state objects or arrays. Use immutable update patterns (e.g., spread operator ..., Object.assign(), Immer.js).
gotcha The `serviceMiddleware.CALL_SERVICE` constant must be used as the `type` property of the action object when dispatching service calls. Forgetting this or using a different string will prevent the middleware from intercepting the action. The `serviceMiddleware` object itself is a named export, and `CALL_SERVICE` is a property on it.
fix Ensure actions meant for the service middleware have `type: serviceMiddleware.CALL_SERVICE` and the correct payload shape.
breaking When upgrading Redux itself to version 4.0 or higher, the signature for custom Redux middleware was simplified. While `inst-redux-service-middleware` should be compatible, any custom middleware you've written might need to be updated to the `({ getState, dispatch }) => next => action` signature.
fix Review the Redux documentation for middleware changes in Redux 4.x and adapt custom middleware if necessary. This package itself should be updated by its maintainers to reflect these changes.
gotcha Services registered with `createServiceMiddleware` must be plain JavaScript objects with methods. Ensure that these methods do not directly mutate the Redux state, as Redux strictly enforces immutability. Side effects should produce new state via dispatched actions.
fix Design service methods to be pure functions that return data or promises, or dispatch actions that trigger state updates through reducers, rather than modifying state directly.
gotcha Managing complex asynchronous flows (e.g., sequential calls, debouncing, cancellations) purely through the basic action shape of this middleware can become cumbersome. For advanced side effect management, consider combining this middleware with solutions like `redux-saga` or `redux-observable`, or leveraging `redux-thunk` if it fits the async pattern.
fix For simple API calls, this middleware is sufficient. For intricate async orchestration, consider using a dedicated side-effect library or Redux Toolkit's built-in `createAsyncThunk`.
gotcha As the JavaScript ecosystem shifts towards ES Modules (ESM), importing CommonJS (CJS) modules, or dealing with hybrid packages, can lead to unexpected import errors (e.g., 'Named export not found' or issues with `require`). While this package likely supports CJS, modern bundlers and Node.js environments often default to ESM.
fix Prefer `import` syntax. If encountering issues in Node.js ESM projects, check the package's `package.json` for `exports` field and ensure your build tools (e.g., Webpack, Rollup) are configured correctly for module resolution. As a last resort, try `import pkg from 'inst-redux-service-middleware'; const { createServiceMiddleware } = pkg;`.
npm install inst-redux-service-middleware
yarn add inst-redux-service-middleware
pnpm add inst-redux-service-middleware

This quickstart demonstrates how to set up `inst-redux-service-middleware` with a Redux store, register services, and dispatch actions to invoke service methods, including handling success and error callbacks.

import { createStore, applyMiddleware } from 'redux';
import { createServiceMiddleware, serviceMiddleware } from 'inst-redux-service-middleware';

// A simple reducer
const rootReducer = (state = { data: null, error: null }, action) => {
  switch (action.type) {
    case 'FETCH_SUCCESS':
      return { ...state, data: action.payload, error: null };
    case 'FETCH_ERROR':
      return { ...state, error: action.payload, data: null };
    default:
      return state;
  }
};

// Define a simple service
const myService = {
  async fetchData(id) {
    try {
      const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`);
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      return await response.json();
    } catch (error) {
      console.error('Service error:', error);
      throw error; // Re-throw to be caught by the action creator if needed
    }
  },
  getGreeting(name) {
    return `Hello, ${name}!`;
  }
};

// Create the service middleware and register services
const serviceMid = createServiceMiddleware({
  api: myService,
  greeter: myService
});

// Create the Redux store with the middleware
const store = createStore(rootReducer, applyMiddleware(serviceMid));

console.log('Initial state:', store.getState());

// Dispatch an action to call a service method
store.dispatch({
  type: serviceMiddleware.CALL_SERVICE,
  payload: {
    service: 'api',
    method: 'fetchData',
    args: [1]
  },
  meta: { 
    onSuccess: (data) => ({ type: 'FETCH_SUCCESS', payload: data }),
    onError: (error) => ({ type: 'FETCH_ERROR', payload: error.message })
  }
});

// Example of another service call
store.dispatch({
  type: serviceMiddleware.CALL_SERVICE,
  payload: {
    service: 'greeter',
    method: 'getGreeting',
    args: ['World']
  },
  meta: {
    onSuccess: (greeting) => console.log('Greeting:', greeting),
    onError: (error) => console.error('Greeting error:', error)
  }
});