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)