redux-api-middleman-formdata-fix
v0.0.8
Published
This is a forked version of 'redux-api-middleman'. I add a few lines to support POSTing with FormData. Original description: A Redux middleware making sending request a breeze.
Downloads
5
Readme
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 byaxios
LICENCE:
MIT