Have ideas to improve npm?Join in the discussion! »

    redux-cached-api-middleware

    0.3.0 • Public • Published

    redux-cached-api-middleware

    Redux module that makes working with APIs a breeze.

    npm version Build Status codecov.io License: MIT gzip size size module formats: umd, cjs and es semantic-release Greenkeeper badge code style: prettier

    Table of Contents

    Why

    Caching API responses can greatly increase UX by saving network bandwidth and not showing loaders for the same resources all over again while user navigates the application. You can also create a fluid returning UX in combination with persistance libraries, e.g., redux-persist.

    The redux-api-middleware library is pretty standardized and popular way to interact with APIs using redux, that's why it was chosen as a base for this package.

    Installation

    1. Install dependencies:
    $ npm install --save redux-cached-api-middleware redux-api-middleware redux-thunk

    or

    $ yarn add redux-cached-api-middleware redux-api-middleware redux-thunk

    * You can also consume this package via <script> tag in browser from UMD build. The UMD builds make redux-cached-api-middleware available as a window.ReduxCachedApiMiddleware global variable.

    1. Setup redux:
    import { createStore, combineReducers, applyMiddleware } from 'redux';
    import thunk from 'redux-thunk';
    import { apiMiddleware } from 'redux-api-middleware';
    import api from 'redux-cached-api-middleware';
    import reducers from './reducers';
     
    const store = createStore(
      combineReducers({
        ...reducers,
        [api.constants.NAME]: api.reducer,
      }),
      applyMiddleware(thunk, apiMiddleware)
    );

    Example

    A simple ExampleApp component that invokes API endpoint on mount with TTL_SUCCESS cache strategy of 10 minutes. This means that if items were fetched in the past 10 minutes successfully, the cached value will be returned, otherwise new fetch request will happen.

    import React from 'react';
    import PropTypes from 'prop-types';
    import { connect } from 'react-redux';
    import api from 'redux-cached-api-middleware';
    import Items from './Items';
    import Error from './Error';
     
    class ExampleApp extends React.Component {
      componentDidMount() {
        this.props.fetchData();
      }
     
      render() {
        const { result } = this.props;
        if (!result) return null;
        if (result.fetching) return <div>Loading...</div>;
        if (result.error) return <Error data={result.errorPayload} />;
        if (result.successPayload) return <Items data={result.successPayload} />;
        return <div>No items</div>;
      }
    }
     
    ExampleApp.propTypes = {
      fetchData: PropTypes.func.isRequired,
      result: PropTypes.shape({}),
    };
     
    const CACHE_KEY = 'GET/items';
     
    const enhance = connect(
      state => ({
        result: api.selectors.getResult(state, CACHE_KEY),
      }),
      dispatch => ({
        fetchData() {
          return dispatch(
            api.actions.invoke({
              method: 'GET',
              headers: { Accept: 'application/json' },
              endpoint: 'https://my-api.com/items/',
              cache: {
                key: CACHE_KEY,
                strategy: api.cache
                  .get(api.constants.CACHE_TYPES.TTL_SUCCESS)
                  .buildStrategy({ ttl: 10 * 60 * 1000 }), // 10 minutes
              },
            })
          );
        },
      })
    );
     
    export default enhance(ExampleApp);

    API

    API Config

    DEFAULT_INVOKE_OPTIONS

    The default redux-api-middleware RSAA options object that later will be merged when calling every invoke action - e.g.:

    api.config.DEFAULT_INVOKE_OPTIONS = {
      method: 'GET',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
    }

    * Options get merged using Object.assign({}, DEFAULT_INVOKE_OPTIONS, invokeOptions) in invoke action.

    DEFAULT_CACHE_STRATEGY

    The default caching strategy that will be used when calling every invoke action - e.g.:

    api.config.DEFAULT_CACHE_STRATEGY = api.cache
      .get(api.constants.CACHE_TYPES.TTL_SUCCESS)
      .buildStrategy({ ttl: 600000 });

    Redux Actions

    invoke()

    Call API endpoints anywhere and retrieve data with redux selectors.

    dispatch(api.actions.invoke(
      options: InvokeOptions,
    ));

    The invoke action response will be undefined if there was a valid cached value in redux state, otherwise invoke will return redux-api-middleware response.

    InvokeOptions is an extended version of redux-api-middleware options. You can use invoke like an RSAA action wrapper without any caching. To start using caching possibilities you need pass cache object. You have to provide unique key value and either a caching strategy or shouldFetch function.

    • Cache strategy - use one of pre-defined caching strategies to defined at what state resource is valid or not:
    api.actions.invoke({
      method: 'GET',
      headers: { Accept: 'application/json' },
      endpoint: 'https://my-api.com/items/',
      cache: {
        key: 'GET/my-api.com/items',
        strategy: api.cache
          .get(api.constants.CACHE_TYPES.TTL_SUCCESS)
          .buildStrategy({ ttl: 600000 }), // 10 minutes
      },
    })
    • shouldFetch function - a custom function to defined when resource valid:
    api.actions.invoke({
      method: 'GET',
      headers: { Accept: 'application/json' },
      endpoint: 'https://my-api.com/items/',
      cache: {
        key: 'GET/my-api.com/items',
        shouldFetch({ state: CachedApiState }) {
          // Define your logic when the resource should be re-fetched
          return true;
        }
      },
    })

    * Check getResult selector docs for CachedApiState structure.

    invalidateCache()

    If you're restoring redux state from offline storage, there might be some interrupted fetch requests - which can restore your app in a broken state. You can invalidate all the cached redux state, or selectively with cacheKey.

    dispatch(api.actions.invalidateCache(
      cacheKey: ?string // unique cache key
    ));

    clearCache()

    Clear all the cached redux state, or selectively with cacheKey.

    dispatch(api.actions.clearCache(
      cacheKey: ?string // unique cache key
    ));

    Redux Selectors

    getResult()

    Select all information about API request.

    const response: ?CachedApiState = api.selectors.getResult(
      state: Object, // redux state
      cacheKey: string // unique cache key
    );

    The selected CachedApiState object has a structure of:

    {
      fetching: boolean, // is fetching in progress
      fetched: boolean, // was any fetch completed
      error: boolean, // was last response an error
      timestamp: ?number, // last response timestamp
      successPayload: ?any, // last success response payload
      errorPayload: ?any, // last error response payload
    }

    * If getResult response is undefined it means the API request wasn't initialized yet.

    Caching Strategies

    • SIMPLE_SUCCESS - uses previous successful fetch result
    const strategy = api.cache
      .get(api.constants.CACHE_TYPES.SIMPLE_SUCCESS)
      .buildStrategy();
    • SIMPLE - uses any previous payload fetch result
    const strategy = api.cache
      .get(api.constants.CACHE_TYPES.SIMPLE)
      .buildStrategy();
    • TTL_SUCCESS - uses previous successful fetch result if time to live (TTL) was not reached
    const strategy = api.cache
      .get(api.constants.CACHE_TYPES.TTL_SUCCESS)
      .buildStrategy({ttl: 1000});
    • TTL - uses any previous fetch result if TTL was not reached
    const strategy = api.cache
      .get(api.constants.CACHE_TYPES.TTL)
      .buildStrategy({ttl: 1000});

    Demos

    Other Solutions

    There are other solutions if redux-cached-api-middleware doesn't fit your needs:

    References

    License

    MIT

    Install

    npm i redux-cached-api-middleware

    DownloadsWeekly Downloads

    113

    Version

    0.3.0

    License

    MIT

    Unpacked Size

    74 kB

    Total Files

    31

    Last publish

    Collaborators

    • avatar