npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

redux-api-middleman

v3.2.3

Published

A Redux middleware making sending request a breeze

Downloads

343

Readme

Build Status npm version minzip bundle size Greenkeeper badge

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