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 🙏

© 2026 – Pkg Stats / Ryan Hefner

api-actions

v1.0.0

Published

Redux middleware to simplify async actions when communicating with an API

Readme

About

This package provides a simple interface to dispatch async FSA-compliant actions based on API responses.

Installation

npm i -S api-actions

In the file where you initialize your redux store, import the api-actions middleware, pass it your axios instance, and include it as middleware.

import APIActions from 'api-actions';
import {configureStore} from '@reduxjs/toolkit';
import {combineReducers} from 'redux';
import axios from 'axios'

const axiosInstance = axios.create({...});

export default configureStore({
  reducer: combineReducers({...}),
  middleware: (gdm) => gdm().concat(APIActions(axiosInstance)),
})

Usage

The api-actions middleware takes an axios instance upon initalization and intercepts a RSAA (Redux Standard API-Calling Action). These are actions identified by the presence of an [RSAA] key, where RSAA is a String constant defined by the api-actions middleware. To create a RSAA action compatible with the api-actions middleware, you must use the createAPIAction method, which takes an object describing your API request as a parameter. Here is an example:

import { createAPIAction } from 'api-actions';
...
dispatch(createAPIAction({
  path: 'http://example.com/create_action',
  method: 'GET',
  types: ['REQUEST', 'SUCCESS', 'FAIL']
})

The createAPIAction parameter object is typed as the following:

{
  path: string;
  method: HTTPMethod;
  body?: PlainObject | ((getState: () => any) => PlainObject);
  types: TypeArray;
  onReqSuccess?: (getState: () => any, res: AxiosResponse, axios: AxiosInstance) => void;
  onReqFail?: (getState: () => any, err: AxiosError, axios: AxiosInstance) => void;
  config?: AxiosRequestConfig;
}

Let's dissect this.

  • Path: This field is self-explanatory - this is where you pass the URL for your request. Note: this field may also represent just an endpoint if you have set a baseURL for the axios instance passed to api-actions.
  • Method: One of the HTTP methods.
  • Body: This may be a JSON object or a function that takes the current state of your redux store as an argument and returns a JSON object.
  • Types: The TypeArray is defined as the following:
[string, string | RequestDescriptor, string]

It is an array of three elements, where the first string is the type of the action to be dispatched before the request is made - this may be used to handle loading. The second element describes the action to be dispatched if the API returns a response with a status code in the 2xx range. The last element specifies the type of the action dispatched on failure (AxiosError, NetworkError or InternalError - more about error handling in a bit). If the second element is just a string ([TYPE] of action dispatched on success), the dispatched action will default to the following:

{ 
  type: [TYPE],
  payload: res.data // res is the AxiosResponse recieved
}

However, there may be situations in which you may want to customize the payload of the above action - let's say you want to dispatch res.statusCode instead. This can be done by passing a RequestDescriptor instead of a string. A RequestDescriptor is an object of the following shape:

{
  type: string;
  payload: (getState: () => any, res: AxiosResponse) => PlainObject;
}

Hence, to dispatch res.statusCode as the payload, you would use the following array to define types:

['REQUEST_TYPE', 
  { 
    type: 'SUCCESS_TYPE', 
    payload: (_, res) => res.statusCode 
   }, 
 'FAIL_TYPE']

This types definition would dispatch the following before the request is made:

{ type: 'REQUEST_TYPE' }

On recieving a successful response, the dispatched action will be of the following form:

{ 
  type: 'SUCCESS_TYPE', 
  payload: '2xx' // res.statusCode
}

Finally, the action dispatched on error will be the following (more details about error handling here):

{
  type: 'FAIL_TYPE', 
  payload: [ERROR OBJECT], 
  error: true 
}
  • onReqSuccess: This is a function that runs to completion upon recieving a successful response and before dispatching the success action. It takes the current state of the redux store, the response object and the axios instance as arguments. One example usage of this function could be to set new default headers to the axios instance based on the response. This would be acheived by the following function:
...
onReqSuccess: (_, res, axios) => axios.defaults.headers.common['Authorization'] = `BEARER ${res.data.new_access_token}`
...
  • onReqFail: Similar to the previous function, this runs to completion upon recieving an AxiosError (either an API error or network error) and before dispatching the error action.
  • config: One benefit of passing a axios instance to api-actions is that this allows to set default AxiosRequestConfig options to ba applied to all your requests. However, some requests may need to override these defaults. To do this, you could pass in a custom config object.

Error Handling

In accordance with FSA, in the case of an error, an action of the following form is returned:

{ 
  type: [FAIL_TYPE],
  payload: [ERROR OBJECT],
  error: true
}

There are three types of error classes that api-actions may return - InternalError, NetworkError, RequestError. InternalError is thrown when a user-defined function fails (such as the function passed in the request descriptor), NetworkError is thrown when the API is unreachable, and RequestError is thrown when the API returns a response with statusCode in the 4xx/5xx range. Each of these error classes extend the CustomError class, which defined as the following:

class CustomError extends Error {
  __error: any;
  constructor(name: string, message: string, err: any) {
    super(message);
    this.name = name;
    this.__error = err;
  }
}

The __error property wraps the original error thrown. For example, when an API returns a response with statusCode 400 (i.e Bad Request), the following action is dispatched:

{
  type: [FAIL_TYPE],
  payload: {
    name: 'RequestError',
    message: 'Request failed with error code 400',
    __error: [AxiosError object]
  },
  error: true
}

Testing

This package is unit tested using the mocha and chai libraries. Utilizing axios-mock-adapter and redux-mock-store, most test code follows this format:

// This snippet tests whether a FAIL_ACTION_TYPE is dispatched on receiving a 404 error from the API
it('should dispatch FAIL_ACTION_TYPE on RequestError', async () => {
    // define mock endpoint
    mockAxiosClient.onGet('/test').reply(404);
  
    // array of expected actions to be dispatched by api-actions
    const expectActions = [
      {
        type: 'REQUEST_ACTION_TYPE',
      },
      {
        type: 'FAIL_ACTION_TYPE',
        payload: new RequestError('RequestError: Request failed with status code 404', {}),
        error: true,
      },
    ];
    
    // dispatch APIAction to mock store
    await store.dispatch({
      ...createAPIAction({
        path: '/test',
        types: ['REQUEST_ACTION_TYPE', 'SUCCESS_ACTION_TYPE', 'FAIL_ACTION_TYPE'],
      }),
      type: 'TESTING',
    });

    expect(store.getActions()).shallowDeepEqual(expectActions);
 });

License

MIT

Acknowledgements

This package was inspired by the following sources: