Home Reference Source

src/hofs/interceptor.js

const beforeRequestInterceptors = [];
const afterResponseInterceptors = [];

/**
 * Manage interceptors - to do something before or after every fetch.
 * Use this with {@link addBeforeRequestInterceptor} and {@link addAfterResonseInterceptor}
 * @example
 * import {
 *  addBeforeRequestInterceptor,
 *  addAfterResonseInterceptor,
 *  interceptor
 * } from 'higher-order-fetch/hofs/interceptor'
 *
 * const fetch = interceptor()(window.fetch)
 *
 * addBeforeRequestInterceptor(() => {
 *  Loader.show()
 * })
 * addAfterResonseInterceptor(() => {
 *  Loader.hide()
 * })
 *
 * fetch('http://my.url/data')
 * .then(response => response.json())
 * .then(data => {...})
 */
export const interceptor = () => fetch => async (resource, init) => {
  //run interceptors before each request
  let beforeRequestInterceptorsResult = applyBeforeRequestInterceptors(
    beforeRequestInterceptors
  );
  if (beforeRequestInterceptorsResult === false) {
    throw new Error(
      "Fetch Promise was canceled by interceptor before requested"
    );
  }

  const response = await fetch(resource, init);

  // run interceptors after each requests
  let afterResponseInterceptorsResult = applyAfterResponseInterceptors(
    response,
    afterResponseInterceptors
  );
  if (afterResponseInterceptorsResult === false) {
    throw new Error(
      "Fetch Promise was canceled by interceptor after responded"
    );
  }

  return response;
};

/**
 * To define something to do before every fetch request.<br/>
 * If any interceptor returns false, the process will be stop immediately.<br/>
 * Only work with {@link interceptor}
 * @ignore
 * @param {function} interceptor callback that will be called before every request
 * @returns {function} Callback to remove added interceptor
 */
export function addBeforeRequestInterceptor(interceptor) {
  beforeRequestInterceptors.push(interceptor);
  return () => {
    const index = beforeRequestInterceptors.indexOf(interceptor);
    beforeRequestInterceptors.splice(index, 1);
  };
}

/**
 * To define something to do after every fetch response.<br/>
 * If any interceptor returns false, the process will be stop immediately.<br/>
 * Only work with {@link interceptor}
 * @ignore
 * @param {function} interceptor callback that will be called after every response
 * @returns {function} Callback to remove added interceptor
 */
export function addAfterResonseInterceptor(interceptor) {
  afterResponseInterceptors.push(interceptor);
  return () => {
    const index = afterResponseInterceptors.indexOf(interceptor);
    afterResponseInterceptors.splice(index, 1);
  };
}

function applyBeforeRequestInterceptors(interceptors) {
  interceptors.forEach(interceptor => {
    try {
      const interceptorResult = interceptor();
      if (interceptorResult === false) {
        console.error(
          "Interceptor ",
          interceptor,
          " has cancel signal. This makes the request stop immediately."
        );
        return false;
      }
    } catch (e) {
      console.error(`Error from interceptor ${interceptor}`, e);
      return false;
    }
  });
}

function applyAfterResponseInterceptors(response, interceptors) {
  interceptors.forEach(interceptor => {
    try {
      const interceptorResult = interceptor(response);
      if (interceptorResult === false) {
        console.error(
          "Interceptor ",
          interceptor,
          " has cancel signal. This makes the request stop immediately."
        );
        return false;
      }
    } catch (e) {
      console.error(`Error from interceptor ${interceptor}`, e);
      return false;
    }
  });
}