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

@simonsmith/redux-api-middleware

v1.0.0

Published

A tiny [Redux](https://redux.js.org/) middleware for simplifying the communication with API endpoints.

Downloads

5

Readme

Redux API middleware

A tiny Redux middleware for simplifying the communication with API endpoints.

Build Status

Why

When writing async action creators that interact with API endpoints (for example with redux-thunk) it is not uncommon for logic to be duplicated. Each creator will typically configure a request via a library such as axios or fetch and dispatch additional actions for loading or error states.

By using middleware these actions can be intercepted and this logic can be centralised, greatly reducing boilerplate and easing testing.

Installation

yarn

yarn add @simonsmith/redux-api-middleware

npm

npm install --save @simonsmith/redux-api-middleware

Quick example

Pass createApiMiddleware a request library as the first argument and any configuration options. Any promise based library can be used such as fetch or ky. You're free to pass your own function to prepare the promise for the middleware (such as first calling response.json() in fetch).

import {createApiMiddleware} from '@simonsmith/redux-api-middleware';
import {configureStore} from 'redux-starter-kit';
import axios from 'axios';

const store = configureStore({
  reducer: state => state,
  middleware: [
    createApiMiddleware(axios, {
      requestDefaults: {
        baseURL: 'http://myapi.com',
      },
    }),
  ],
});

Use the apiRequest helper function to send a request through the middleware. Actions for request start, end and failure will be dispatched as well as calling your onSuccess or onFailure callbacks. These can be used to dispatch additional actions.

import {apiRequest} from '@simonsmith/redux-api-middleware';

// An action creator
export function requestAllUsers() {
  return apiRequest('/users', {
    type: FETCH_USERS,
    onSuccess: dispatchAnotherAction,
  });
}

API

createApiMiddleware(request, options?)

Returns a function that acts as the middleware which can be passed to Redux during store creation.

request: (url, options?) => Promise

This function is used by the middleware to send a network request. It must return a promise that resolves with the payload from the server.

For example when using the fetch function it requires response.json() to be called and the promise returned:

const requestFunc = (url, options) => {
  return fetch(url, options).then(res => res.json());
}

createApiMiddleware(requestFunc),

options?: Object

  • actionTypes (object)
    • start (string) - Dispatched before a request begins default API_REQUEST_START
    • end (string) - Dispatched when a request ends default API_REQUEST_END
    • failure (string) - Dispatched when a request fails default API_REQUEST_FAILURE
  • requestDefaults (object) Options passed to the request library on each request

The options argument passed to request is merged with the values from the apiRequest function and any values in the requestDefaults object provided in the options. This allows things like headers to be provided on all requests by default:

createApiMiddleware(requestFunc, {
  requestDefaults: {
    headers: {
     'Content-Type': 'application/json',
    },
  },
}),

These can be overridden in the second argument to apiRequest if needed.

apiRequest(url, options?)

Creates an action that will be dispatched to the store and intercepted by the middleware.

function updatePost(id, newPost) {
  return apiRequest(`/post/${id}`, {
    type: 'UPDATE_POST',
    onSuccess: getPosts,
    onFailure: logError,
    // `method` and `data` passed to the `request` function
    method: 'PUT',
    data: newPost,
  });
}

url: String

The url that the request will be made to.

options?: Object

Can contain a type, onSuccess and onFailure. Any additional values will be merged with the requestDefaults passed to the request function.

type: String

When a type is provided the middleware will dispatch each of the actions in actionTypes with a payload of the type value. This allows different requests to be differentiated from one another.

onSuccess: Function

When provided this function will be called with the response from the request function. Its return value should be an action as it will be passed to dispatch.

onFailure: Function

When provided this function will be called with the error from the request function. Its return value should be an action as it will be passed to dispatch.

Configuring common request libraries

axios

One of the simplest to configure, the axios object can be passed directly to createApiMiddleware:

createApiMiddleware(axios);

ky

Return a promise from the json method:

const requestFunc = (url, options) => ky(url, options).json();

createApiMiddleware(requestFunc);

fetch

Return a promise from response.json:

const requestFunc = (url, options) => fetch(url, options).then(res => res.json());

createApiMiddleware(requestFunc);

Actions

In addition to the success and failure callbacks there are also actions dispatched when a request is loading or encounters an error. This allows the logic to be centralised in separate reducers.

All actions dispatched conform to the FSA spec.

Loading

When the type option is used the following loading actions are dispatched:

apiRequest('/url', {
  type: 'SOME_ACTION',
});

// actions
{type: 'API_REQUEST_START', payload: 'SOME_ACTION'}
{type: 'API_REQUEST_END', payload: 'SOME_ACTION'}

The payload is set to the type value allowing these actions to be differentiated in a reducer.

Error

If an error is encountered an error action is dispatched:

{type: 'API_REQUEST_FAILURE', payload: Error('some error'), error: true}

Handling loading states in a reducer

It's recommended to create a separate reducer to handle loading actions. This has the added benefit of keeping reducers free from repetitive logic (such as isLoading state):

import {createReducer} from 'redux-starter-kit';

const initialState = {};

export const loadingReducer = createReducer(initialState, {
  API_REQUEST_START: (state, action) => {
    return {
      ...state,
      [action.payload]: true,
    };
  },
  API_REQUEST_END: (state, action) => {
    return {
      ...state,
      [action.payload]: false,
    };
  },
});
// state example
{
  loading: {
    FETCH_PROFILE: true,
    CREATE_USER: false,
  }
}

Components can select the loading state they are interested in and use this value to display a spinner or text to the user.

Contributing

Pull requests are welcome!

Credit