api-client.js

/**
 * ApiClient class is a wrapper around axios.
 *
 * It provides the same set of methods to make requests to the API.
 * Also, it provides a way to add interceptors to the request/response cycle.
 */
class ApiClient {
  /**
   * An instance of axios with a custom config.
   *
   * @type {AxiosInstance}
   */
  #instance;

  /**
   * ApiClient constructor
   *
   * @param {{create: function}|Axios} httpClient - Http client or axios instance
   * @param {AxiosRequestConfig|{}} instanceConfig - Config defaults for the instance
   */
  constructor(httpClient, instanceConfig = {}) {
    this.#instance = httpClient.create(instanceConfig);
  }

  /**
   * Returns default configuration
   *
   * @return {AxiosRequestConfig}
   */
  get defaults() {
    return this.#instance.defaults;
  }

  /**
   * Set default configuration value
   *
   * @param {*} value
   *
   * @example
   * import axios from 'axios';
   *
   * const apiClient = new ApiClient(axios);
   *
   * apiClient.defaults.headers.common['X-Test'] = 'test';
   * api.defaults.baseURL = 'https://example.com';
   * api.defaults.timeout = 100;
   *
   * console.log(api.defaults.headers.common['X-Test'])
   * test
   *
   * console.log(api.defaults.baseURL)
   * https://example.com
   *
   * console.log(api.defaults.timeout)
   * 100
   */
  set defaults(value) {
    this.#instance.defaults = value;
  }

  /**
   * Interceptors getter
   *
   * @return {{request: AxiosInterceptorManager<AxiosRequestConfig>, response: AxiosInterceptorManager<AxiosResponse>}}
   */
  get interceptors() {
    return this.#instance.interceptors;
  }

  //-------
  // Methods to manage Request/Response interceptors
  //-------

  /**
   * Add a request interceptor
   *
   * @param {Function} onFulfilled - Any status code that lie within the range of 2xx cause this function to trigger
   * @param {Function} [onRejected=null] - Any status codes that falls outside the range of 2xx cause this function to trigger
   *
   * @return {Number} An ID used to remove interceptor later
   */
  addRequestInterceptor(onFulfilled, onRejected = null) {
    return this.#instance.interceptors.request.use(onFulfilled, onRejected);
  }

  /**
   * Remove a Request interceptor from the stack
   *
   * @param {Number} id - The ID that was returned by `addRequestInterceptor`
   */
  removeRequestInterceptor(id) {
    this.#instance.interceptors.request.eject(id);
  }

  /**
   * Add a response interceptor
   *
   * @param {Function} onFulfilled Any status code that lie within the range of 2xx cause this function to trigger
   * @param {Function} [onRejected=null] Any status codes that falls outside the range of 2xx cause this function to trigger
   *
   * @return {Number} - An ID used to remove interceptor later
   */
  addResponseInterceptor(onFulfilled, onRejected = null) {
    return this.#instance.interceptors.response.use(onFulfilled, onRejected);
  }

  /**
   * Remove a Response interceptor from the stack
   *
   * @param {Number} id - The ID that was returned by `addResponseInterceptor`
   */
  removeResponseInterceptor(id) {
    this.#instance.interceptors.response.eject(id);
  }

  /**
   * Returns the URL for making request
   *
   * @param {Object} [config] - The config specific for this request (merged with defaults)
   * @return {string}
   */
  getUri(config) {
    return this.#instance.getUri(config);
  }

  //-------
  // Request methods
  //-------

  /**
   * Dispatch a request
   *
   * @param {Object} config - The config specific for this request (merged with defaults)
   * @return {Promise<AxiosResponse<any>>}
   */
  request(config) {
    return this.#instance.request(config);
  }

  /**
   * Performs GET Request
   *
   * @param {String} url - Request url Request url
   * @param {Object} [config={}] - Request config
   * @returns {Promise<AxiosResponse<any>>}
   */
  get(url, config = {}) {
    return this.#instance.get(url, config);
  }

  /**
   * Performs DELETE Request
   *
   * @param {String} url - Request url Request url
   * @param {Object} [config={}] - Request config
   * @returns {Promise<AxiosResponse<any>>}
   */
  delete(url, config = {}) {
    return this.#instance.delete(url, config);
  }

  /**
   * Performs HEAD Request
   *
   * @param {String} url - Request url Request url
   * @param {Object} [config={}] - Request config
   * @returns {Promise<AxiosResponse<any>>}
   */
  head(url, config = {}) {
    return this.#instance.head(url, config);
  }

  /**
   * Performs OPTIONS Request
   *
   * @param {String} url - Request url Request url
   * @param {Object} [config={}] - Request config
   * @returns {Promise<AxiosResponse<any>>}
   */
  options(url, config = {}) {
    return this.#instance.options(url, config);
  }

  /**
   * Performs POST Request
   *
   * @param {String} url - Request url
   * @param {string|{}|ArrayBuffer|ArrayBufferView|URLSearchParams|FormData|File|Blob|Stream|Buffer} [data=null] - The data to be sent as the request body. Browser only: FormData, File, Blob. Node only: Stream, Buffer.
   * @param {Object} [config={}] - Request config
   * @returns {Promise<AxiosResponse<any>>}
   */
  post(url, data = null, config = {}) {
    return this.#instance.post(url, data, config);
  }

  /**
   * Performs PUT Request
   *
   * @param {String} url - Request url
   * @param {string|{}|ArrayBuffer|ArrayBufferView|URLSearchParams|FormData|File|Blob|Stream|Buffer} [data=null] - The data to be sent as the request body. Browser only: FormData, File, Blob. Node only: Stream, Buffer.
   * @param {Object} [config={}] - Request config
   * @returns {Promise<AxiosResponse<any>>}
   */
  put(url, data = null, config = {}) {
    return this.#instance.put(url, data, config);
  }

  /**
   * Performs PATCH Request
   *
   * @param {String} url - Request url
   * @param {string|{}|ArrayBuffer|ArrayBufferView|URLSearchParams|FormData|File|Blob|Stream|Buffer} [data=null] - The data to be sent as the request body. Browser only: FormData, File, Blob. Node only: Stream, Buffer.
   * @param {Object} [config={}] - Request config
   * @returns {Promise<AxiosResponse<any>>}
   */
  patch(url, data = null, config = {}) {
    return this.#instance.patch(url, data, config);
  }

  /**
   * Performs POST Request with FormData
   *
   * @param {String} url - Request url
   * @param {string|{}|ArrayBuffer|ArrayBufferView|URLSearchParams|FormData|File|Blob|Stream|Buffer} [data=null] - The data to be sent as the request body. Browser only: FormData, File, Blob. Node only: Stream, Buffer.
   * @param {Object} [config={}] - Request config
   * @return {Promise<axios.AxiosResponse<any>>}
   */
  postForm(url, data = null, config = {}) {
    return this.#instance.postForm(url, data, config);
  }

  /**
   * Performs PUT Request with FormData
   *
   * @param {String} url - Request url
   * @param {string|{}|ArrayBuffer|ArrayBufferView|URLSearchParams|FormData|File|Blob|Stream|Buffer} [data=null] - The data to be sent as the request body. Browser only: FormData, File, Blob. Node only: Stream, Buffer.
   * @param {Object} [config={}] - Request config
   * @return {Promise<axios.AxiosResponse<any>>}
   */
  putForm(url, data = null, config = {}) {
    return this.#instance.putForm(url, data, config);
  }

  /**
   * Performs PATCH Request with FormData
   *
   * @param {String} url - Request url
   * @param {string|{}|ArrayBuffer|ArrayBufferView|URLSearchParams|FormData|File|Blob|Stream|Buffer} [data=null] - The data to be sent as the request body. Browser only: FormData, File, Blob. Node only: Stream, Buffer.
   * @param {Object} [config={}] - Request config
   * @return {Promise<axios.AxiosResponse<any>>}
   */
  patchForm(url, data = null, config = {}) {
    return this.#instance.patchForm(url, data, config);
  }
}

export { ApiClient };