redux-api-middleman-formdata-fix

0.0.8 • Public • Published

Build Status npm version

Redux API Middleman

A Redux middleware extracting the asynchronous behavior of sending API requests.

Usage:

Get Started:

  • Create the middleware and put into your middleware chain:
import { createStore, applyMiddleware } from 'redux'
import createApiMiddleman from 'redux-api-middleman'
 
let apiMiddleware = createApiMiddleman({
  baseUrl: 'http://api.myapp.com',
})
 
const store = applyMiddleware(
  [ apiMiddleware ]
)(createStore)()
  • Use it in your action creators:
// user action
 
import { CALL_API } from 'redux-api-middleman'
 
export const GETTING_MY_INFO = 'GETTING_MY_INFO'
export const GET_MY_INFO_SUCCESS = 'GET_MY_INFO_SUCCESS'
export const GET_MY_INFO_FAILED = 'GET_MY_INFO_FAILED'
 
export function getMyInfo() {
  return {
    [CALL_API]: {
      method: 'get',
      path: '/me',
      sendingType: GETTING_MY_INFO,
      successType: GET_CONTRACTS_SUCCESS,
      errorType: GET_MY_INFO_FAILED
    }
  }
}
  • Handle it in your reducer:
// user reducer
 
import { GET_CONTRACTS_SUCCESS } from 'actions/users'
 
const defaultState = {}
 
export default function(state = defaultState, action) {
  switch(action.type) {
    case GET_CONTRACTS_SUCCESS:
      return action.response
    default:
      return state
  }
}
 

The code above would send a GET request to http://api.myapp.com/me, when success, it would dispatch an action:

{
  type: GET_CONTRACTS_SUCCESS,
  response: { the-camelized-response-body }
}

Features:

  • Async to Sync: Abstract the async nature of sending API to make it easier to implement/test
  • Universal Rendering Friendly
  • Support chaining(successive) API calls
  • Side Effect Friendly
  • Replay request optionally when failed
  • Tweek request/response format when needed

API Documentation:

Creation:

A middleware can be created like this:

import apiMiddleware from 'redux-api-middleman'
 
apiMiddleware({
  baseUrl: 'https://api.myapp.com',
  errorInterceptor: ({ err, proceedError, replay, getState })=> {
    // handle replay here
  },
  generateDefaultParams: ({ getState })=> {
    return {
      headers: { 'X-Requested-From': 'my-killer-app' },
    }
  },
  maxReplayTimes: 5
})

Options:

baseUrl: The base url of api calls(required)

errorInterceptor(optional):

When provided, this function would be invoked whenever an API call fails. The function signature looks like this:

({ err, proceedError, replay, getState })=> {
 
}

Where:

err is the error object returned by superagent, replay() can be used to replay the request with the same method/parameters, proceedError() can be used to proceed error to reducers

For example, to refresh access token when server responds 401:

({ err, proceedError, replay, getState })=> {
  if(err.status === 401) {
    refreshAccessToken().then((res)=> {
       // here you can pass additional headers if you want
       let headers = {
         'x-access-token': res.token,
       }
       replay({ headers })
     })
  } else {
    proceedError()
  }
}

The code above would do the token refreshing whenever err is 401, and proceed the original error otherwise.

generateDefaultParams(optional):

A function which takes ({ getState }) and returns an object like this:

{
  headers: { 'x-header-key': 'header-val' },
  query: { queryKey: 'query-val' },
  body: { bodyKey: 'body-val' }
}

On each request, the object returned by this function would be merged into the request's header, query, and body, respectively.


Usage In Action Creators:

In Action Creators, we can use the following code to send a single request:

import { CALL_API } from 'redux-api-middleman'
 
export const ON_REQUEST_SUCCESS = 'ON_REQUEST_SUCCESS'
export const ON_REQUEST_FAILED = 'ON_REQUEST_FAILED'
export const ON_SENDING_REQUEST = 'ON_SENDING_REQUEST'
 
export function getInfo({ username }) {
  return {
    extraKey: 'extra-val',
 
    [CALL_API]: {
      method: 'get',
      path: `/users/${username}/info`,
      successType: ON_REQUEST_SUCCESS,
      errorType: ON_REQUEST_FAILED,
      sendingType: ON_REQUEST_FAILED,
      afterSuccess: ({ getState, dispatch, response }) => {
        //...
      },
      afterError: ({ getState, error })=> {
        //...
      }
    }
  }
}

In short, just return an action object with CALL_API.

Options:

method(required):

Http verb to use, can be get, post, put or del

path(optional):

Request path to be concated with baseUrl

url:

Full url of request, will take precedence over path and will ignore baseUrl

camelizeResponse(optional):

Camelize response keys of the request. default to true

Transform { user_name: 'name' } to { userName: 'name' }

decamelizeRequest(optional):

Decamelize request payload keys. default to true

Transform { userName: 'name' } to { user_name: 'name' }

withCredentials(optional):

Enable Access-Control requests or not. default to true

sendingType(optional):

Action type to be dispatched immediately after sending the request

successType(required):

Action type to be dispatched after the API call success

errorType(optional):

Action type to be dispatched after the API call fails

afterSuccess(optional):

A callback function to be invoked after dispatching the action with type successType. ({ getState, dispatch, response }) would be passed into this callback function. This is a good place to handle request-related side effects such as route pushing.

afterError(optional):

A callback function to be invoked after dispatching the action with type errorType. ({ getState, error }) would be passed into this callback function.

Sending Chaining Requests:

To send chaining requests, just return an action with CHAIN_API-keyed object like this:

import { CALL_API, CHAIN_API } from 'redux-api-middleman'
 
export const ON_REQUEST_SUCCESS1 = 'ON_REQUEST_SUCCESS1'
export const ON_REQUEST_SUCCESS2 = 'ON_REQUEST_SUCCESS2'
 
export function getInfo({ username }) {
  return {
    [CHAIN_API]: [
      ()=> {
        return {
          extraKey: 'extra-val',
          [CALL_API]: {
            method: 'get',
            path: `/users/${username}/info`,
            successType: ON_REQUEST_SUCCESS1
          }
        }
      },
      (responseOfFirstReq)=> {
        return {
          [CALL_API]: {
            method: 'get',
            path: `/blogs/${responseOfFirstReq.blogId}`,
            successType: ON_REQUEST_SUCCESS2
          }
        }
      }
    ]
  }
}

In the code above, we send an API to /users/${username}/info to fetch user info containing a key blogId. After the first request is finished, we then send the second request with the blogId returned by server.


Usage In Reducers:

During the life cycle of an API call, several types of actions would be dispatched:

sendingType action:

After the request has been sent, an action of type sendingType would be dispatched immediately. The action would contain the key-val pairs other than CALL_API in the action object.

For example, if our action object looks like this:

{
  extraKey1: 'extra-val-1',
  extraKey2: 'extra-val-2',
  [CALL_API]: {
    ...
  }
}

then the sendingType action would be:

{
  type: sendingType,
  extraKey1: 'extra-val-1',
  extraKey2: 'extra-val-2'
}

successType action:

After the server responds successfully, an action of type successType would be dispatched. The action would contain:

  • the key-val pairs other than CALL_API in the action object
  • an extra response key, with its value be the server response

For example, if the server responds with a body like this:

{
  responseKey: 'response-val'
}

then the successType action would be:

{
  type: successType,
  extraKey1: 'extra-val-1',
  extraKey2: 'extra-val-2',
  response: {
    responseKey: 'response-val'
  }
}

errorType action:

After the server responds fails, an action of type errorType would be dispatched. The action would contain:

  • the key-val pairs other than CALL_API in the action object
  • an extra error key, with its value be the error object returned by axios

LICENCE:

MIT

Readme

Keywords

none

Package Sidebar

Install

npm i redux-api-middleman-formdata-fix

Weekly Downloads

1

Version

0.0.8

License

ISC

Unpacked Size

396 kB

Total Files

12

Last publish

Collaborators

  • chiunhau