DEV Community

Warren
Warren

Posted on

Customising redux-api-middleware calls

I've been using the redux-api-middleware for contacting a secure API and because of the need for an Authorization header to be sent with each request found that I was needing to use function values for most of my actions such as the following.

{
    [RSAA]: {
        headers: state => {
            return {
                Authorization: state.authorizationToken
            }
        },
        endpoint: "/my/api/endpoint,
        method: "GET"
    }
}

The headers property will accept either an object, or a function which returns an object. In this case I was using the latter to retrieve the authorization token and adding it to each request. Continuing like this would mean each new request needed the same boilerplate to be repeated each time. To solve the problem I decided to use a custom redux middleware.

Custom Middleware

Middleware is formulated as a pipeline of many middlewares, each passing the dispatched action on to the next middleware before the reducers handle it. If we create a custom middleware that gets to the actions before the redux-api-middleware gets a chance then we can manipulate the dispatched action and make the changes apply to all of our requests removing the need for all that repeated code.

One solution here would be to create a function that adds the Authorization header. This could look something like this

const apiAuthorizationMiddleware = (store: any) => (next: any) => (action: any) => {
    // Pass to next middleware if not a redux-api-middleware action
    if (!action[RSAA]) {
        return next(action)
    }

    return {
        ...action,
        [RSAA]: {
            ...action[RSAA],
            headers: {
                ...action[RSAA].headers,
                Authorization: store.getState().authorizationToken
            }
        }
    }
}

But this would mean that you would always be adding that header. What about requests to different services? You don't want to be passing your "secure" API authorization token around all over the place. Instead what I'm going to do is replace the RSAA with a different value that will identify the requests I want to add the header to. This means my new action will look like this.

{
    [MY_AUTHORIZED_REQUEST]: {
        endpoint: "/my/api/endpoint,
        method: "GET"
    }
}

Note how I've replaced [RSAA] with a new value. That value is what we're going to change our custom middleware to look for.

import { RSAA } from 'redux-api-middleware'

export const MY_AUTHORIZED_REQUEST = "@@authorized_request"

export const apiAuthorizationMiddleware = (store: any) => (next: any) => (action: any) => {
    if (!action[MY_AUTHORIZED_REQUEST]) {
        return next(action)
    }

    const { [MY_AUTHORIZED_REQUEST]: request, ...newAction} = action

    const headers = request.headers ? {...request.headers} : {}
    const state = store.getState()
    headers["Authorization"] = state.authorizationToken

    request.headers = headers

    newAction[RSAA] = request
    return next(newAction)
}

export default MY_AUTHORIZED_REQUEST

Adding this middleware as well as the redux-api-middleware now means we can fetch [RSAA] requests without adding the authorization token header, and [MY_AUTHORIZED_REQUEST] requests with the authorization token header added from the value in the redux state.
This is just one example of what can be done with the middleware, you can change the request in any way you like, for example adding additional query string parameters, or changing the base url.

Setting up the store

Applying the custom middleware is done by adding it in to the pipeline ahead of the redux-api-middleware function along with any other middleware you use.

let createStoreWithMiddleware = applyMiddleware(apiAuthorizationMiddleware, apiMiddleware, routerMiddleware(history), thunkMiddleware)(createStore)
const store = createStoreWithMiddleware(reducer(history), initialState);

Top comments (0)