API Middleware for Redux

API Middleware for Redux

For many apps that use Redux, you will find yourself stuck in a problem of how to make asyncronous API calls. One solution is the popular library Redux-Thunk. Another solution is to make a custom middleware that intercepts API requests, makes the API calls, and dispatches the success or failure actions.

In general Middleware allows you to modify or intercept inputs and outputs before the input or output reaches it’s destination. With Redux, middleware intercepts all dispatched actions. We can see this by taking a look at the redux thunk code.

function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

And that’s it! All Thunk does is intercept an action and if it’s a function, it gives it access to the dispatch and getState redux functions.

If we wanted to make our own middleware to abstract out our logic of making API calls our action creator might look like this before with Thunk.

function saveAstronaut(astronaut) {
  return function(dispatch) {
    jQuery.ajax({
      url: "/api/astronauts",
      data: post,
      success: function(data) {
        dispatch({
          type: "CREATE_ASTRONAUT_SUCCESS",
          data: data
        });
      },
      failure: function(data) {
        dispatch({
          type: "CREATE_ASTRONAUT_FAILURE",
          data: data
        });
      }
    });
  };
}

This makes it very simple to get started but the downside is now all your action creators that interact with the API need to know about how the requests get sent, as opposed to having somewhere that has the logic of sending the requests. As your app gets bigger there will be a lot of duplicated network interaction code.

You can extract this logic and remove the redux thunk dependency at the same time. First lets create the action creator.

const saveAstronaut(astronaut) => {
  return {
    type: "API",
    payload: {
      url: "/api/astronauts",
      method: 'POST',
      params: post
      success: SAVE_ASTRONAUT_SUCCESS,
      failure: SAVE_ASTRONAUT_FAILURE
    }
  };
};

This action creator is a lot more simple, making it easier to test and easier to extend.

The middleware that this relies on to work looks not terribly different from the Thunk middleware.

const apiMiddleware = ({ dispatch }) => next => action => {
    if ( action.type !== API ) {
        return next(action);
    }

    jQuery.ajax({
      url: action.url,
      data: action.params,
      method: action.method,
      success: function(data) {
        dispatch({
          type: action.payload.success,
          data: data
        });
      },
      failure: function(data) {
        dispatch({
          type: action.payload.failure,
          data: data
        });
      }
    });
};

All that’s left is to replace Thunk in the middleware stack and you can run yarn remove redux-thunk.