Skip to content
/ fennch Public

Modern fetch-based axios-like HTTP client for the browser and node.js

License

Notifications You must be signed in to change notification settings

soal/fennch

Repository files navigation

Fennch

Modern fetch-based HTTP client for the browser.

npm version

Fennch is:

fetch + request abortion + timeout support + request and response interceptions

Quickstart

Basic usage

import Fennch from "fennch";
const api = Fennch({
    baseUri: "http://awesome.app/api"
})
async function apiCall() {
    const result = await api.get("/awesome-data", {
        params: {
            awesome: "really",
            params: "cool"
        }
    });
    /* ###### Under the hood ######
        const result = await fetch("http://awesome.app/api/awesome-data?awesome=really&params=cool", {
            method: "GET"
        })
        return {
            ...response,
            body: await response.json() // if Content-Type is 'application/json'
        }
      #############################
    */
    /*
     `result` is a FResponse object which is Proxy that wraps native Response object.
     `result.headers` and `result.body` is already parsed and can accessed right away.
    */
    console.log(result.body) => /* => Awesome response! */
}

Request abortion

const api = Fennch({
  baseUri: "http://awesome.app/api"
});

const MySuperComponent = {
  currentRequest: null,

  handleUserAbort(req) {
    this.currentRequest.abort();
  },

  async apiCall() {
    this.currentRequest = api.get("/awesome-data", {
      params: {
        awesome: "really",
        params: "cool"
      }
    });
    let result;
    try {
      result = await currentRequest;
    } catch (err) {
      result = err;
    }
    return result;
  }
};

// I want make a request
MySuperComponent.apiCall()
  .then(res => res)
  .catch(err => {
    console.log(err); // => 'Request aborted'
  });

// Oh, wait, I changed my mind!
MySuperComponent.handleUserAbort();

Timeout

// Global timeout
const api = Fennch({
  baseUri: "http://awesome.app/api",
  timeout: 10000
});

async function apiCall() {
  try {
    await api.get("/awesome-data", {
        params: {
            awesome: "really",
            params: "cool"
        }
    });
  } catch (err) {
    // If request pednding more than 10 sec
    console.log(err.toString()) // -> "Timeout exceeded"
  }
}

// Timeout per-request, overrides global value
async function apiCallWithTimeout() {
  try {
    await api.get("/awesome-data", {
        timeout: 20000,
        params: {
            awesome: "really",
            params: "cool"
        }
    });
  } catch (err) {
    // If request pednding more than 20 sec
    console.log(err.toString()) // -> "Timeout exceeded"
  }
}

Interceptors

You can register any number of interceptors using register() method. It returns function that can be used to unregister this interceptor.

const unregister = fennch.interceptor.register({
  request(request) {}, // Must return FRequest object, for example `request` that passed as an argument
  requestError(error) {},
  response(response) {}, // Must return FResponse object, for example `request` that passed as an argument
  responseError(error) {}
})

unregister() // unregister interceptor

Simple example:

const api = Fennch({
  baseUri: "http://awesome.app/api"
});

const unregister = api.interceptor.register({
  request(request) {
    /* Making some tweaks in request */
    /*...*/
    return request // Interceptor *must* return request 
  },
  requestError(request) {
    /* Making some tweaks in request */
    /*...*/
    return Promise.resolve(request)
  },
  response(response) {
    /* Making some tweaks in response */
    /*...*/
    return response // Interceptor *must* return response 
  },
  responseError(err) {
    /* If request is aborted adding `cancel` property to error */
    if (err.toString() === 'AbortError') {
      err.cancel = true
      return Promise.reject(err)
    }
    if (!err.response && err.message === 'Network Error') {
      err = networkErrorResolver(err)
      return Promise.resolve(err)
    }
    return err
  }
})

Example for refreshing authorization token using interceptor:

const accessToken = "some_access_token"
const refreshToken = "some_refresh_token"
const api = Fennch({
  baseUri: "http://awesome.app/api",
  headers: {
    "Content-Type": "application/json",
    Accept: 'application/json',
    Authorization: `Bearer ${accessToken}`
  },
});

api.interceptors.register({
  async response(response) {
    if (response.status === 401) {
      try {
        const refreshed = await api.post('/refresh_token', {
          headers: {
            Authorization: `Bearer ${refreshToken}`
          }
        })
        const newAccessToken = refreshed.body.token
        const request = response.request
        request.headers.Authorization = `Bearer ${newAccessToken}`
        return api.req(request)
      } catch (error) {
        return Promise.reject(error)
      }
    }
  }
})

About

Modern fetch-based axios-like HTTP client for the browser and node.js

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published