Templates
Axios

Axios Setup

Install

npm install axios 
yarn add axios
bun install axios

Setup

// axiosClient.ts
import axios, {
  AxiosError,
  AxiosInstance,
  AxiosResponse,
  HttpStatusCode,
  InternalAxiosRequestConfig,
} from 'axios'
 
export interface IResposeError {
  statusCode: number
  message: string
  error: string
}
export interface ICustomError<D = unknown> extends InternalAxiosRequestConfig<D> {
  _retry?: boolean
}
 
/**
 * Axios client to handle the requests and responses
 * It also handles the token refresh
 * @class axiosClient
 * @constructor baseURL - The base URL for the API
 * @method setupInterceptors - Method to setup the request and response interceptors
 * @method onRequest - Method to add the token to the request
 */
export default class AxiosClient {
  private axiosInstance: AxiosInstance
  private store = store.getState()
 
  constructor(baseURL: string | undefined) {
    this.axiosInstance = axios.create({
      baseURL,
      withCredentials: true,
      timeout: 10000,
    })
    this.onRequest = this.onRequest.bind(this)
    this.onResponse = this.onResponse.bind(this)
    this.setupInterceptors()
  }
 
  /**
   * Method to setup the request and response interceptors
   * @method setupInterceptors
   * @return {void}
   */
  private setupInterceptors(): void {
    this.axiosInstance.interceptors.request.use(this.onRequest, this.onRequestError)
    // Add the response interceptor
    this.axiosInstance.interceptors.response.use(this.onResponse, this.onResponseError)
  }
 
  // ADD A REQUEST INTERCEPTOR
  /**
   * Request interceptor to add the token to the request
   */
  private onRequest(config: InternalAxiosRequestConfig): InternalAxiosRequestConfig {
    return config
  }
  /**
   * Request interceptor to handle the request error
   */
  private onRequestError(error: AxiosError): Promise<AxiosError> {
    return Promise.reject(error)
  }
 
  // ADD A RESPONSE INTERCEPTOR
  /**
   * Response interceptor to handle the response
   */
  private async onResponse(response: AxiosResponse) {
    return Promise.resolve(response)
  }
  /**
   * Response interceptor to handle the response error
   * It also handles the token refresh
   * @method onResponseError
   */
  private async onResponseError(error: AxiosError<IResposeError>) {
    return Promise.reject(error)
  }
 
  public getAxiosInstance(): AxiosInstance {
    return this.axiosInstance
  }
}
// restfulAPI.ts
import { AxiosResponse } from 'axios'
 
import AxiosClient from './axiosClient'
 
type TID = string | number
 
interface IRestfulOptions {
  path?: string
}
 
export interface IRestfulAPIConfig {
  path: string
  method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
}
 
/**
 * RestfulAPI class is a base class for all restful API classes
 * It contains all the basic methods for making restful API requests
 * It uses AxiosClient to make the requests
 * @method getRequest - for making GET requests
 * @method getRequestById - for making GET requests by id
 * @method postRequest - for making POST requests
 * @method putRequest - for making PUT requests
 * @method patchRequest - for making PATCH requests
 * @method deleteRequest - for making DELETE requests
 * @example class UserAPI extends RestfulAPI {
 *  path = '/users'
 * }
 * const userAPI = new UserAPI()
 * userAPI.getRequest()
 * userAPI.getRequestById(1)
 * userAPI.postRequest({name: 'John Doe'})
 * userAPI.putRequest(1, {name: 'John Doe'})
 * userAPI.patchRequest(1, {name: 'John Doe'})
 * userAPI.deleteRequest(1)
 */
export class RestfulAPI {
  protected path = ''
  /**
   * Axios client to make the requests
   */
  protected axiosClient = new AxiosClient(API_URL || '').getAxiosInstance()
 
  /**
   * getRequest is a method for making GET requests
   * @param params - query parameters
   * @param options - path to the endpoint
   * @returns Promise<T>
   */
  public getRequest = <T>(params = {}, options: IRestfulOptions = {}): Promise<T> => {
    const { path = '' } = options
    return this.handleRequest<T>(
      {
        path: this.path + path,
        method: 'GET',
      },
      params,
    )
  }
 
  /**
   * getRequestById is a method for making GET requests by id
   * @param id - id of the resource
   * @param options - path to the endpoint
   * @returns Promise<T>
   */
  public getRequestById = <T>(id: TID, options: IRestfulOptions = {}): Promise<T> => {
    const { path = '' } = options
    return this.handleRequest<T>({
      path: this.path + `${path}/${id}`,
      method: 'GET',
    })
  }
 
  /**
   * postRequest is a method for making POST requests
   * @param body - request body
   * @param options - path to the endpoint
   * @returns Promise<T>
   */
  public postRequest = <T>(body = {}, options: IRestfulOptions = {}): Promise<T> => {
    const { path = '' } = options
    return this.handleRequest<T>(
      {
        path: this.path + `${this.path}${path}`,
        method: 'POST',
      },
      body,
    )
  }
 
  /**
   * putRequest is a method for making PUT requests
   * @param id - id of the resource
   * @param body - request body
   * @param options - path to the endpoint
   * @returns Promise<T>
   */
  public async putRequest<T>(id: TID | null, body = {}, options: IRestfulOptions = {}): Promise<T> {
    const { path: Path } = this.optionsParser(options)
    const path = id ? `${Path ?? ''}/${id}` : (Path ?? '')
    return this.handleRequest<T>(
      {
        path: this.path + path,
        method: 'PUT',
      },
      body,
    )
  }
 
  /**
   * patchRequest is a method for making PATCH requests
   * @param id - id of the resource
   * @param body - request body
   * @param options - path to the endpoint
   * @returns Promise<T>
   */
  public async patchRequest<T>(
    id: TID | null,
    body = {},
    options: IRestfulOptions = {},
  ): Promise<T> {
    const { path: Path } = this.optionsParser(options)
    const path = id ? `${Path ?? ''}/${id}` : (Path ?? '')
    return this.handleRequest<T>(
      {
        path: this.path + path,
        method: 'PATCH',
      },
      body,
    )
  }
 
  /**
   * deleteRequest is a method for making DELETE requests
   * @param id - id of the resource
   * @param options - path to the endpoint
   * @returns Promise<T>
   */
  public deleteRequest = async <T>(id: TID | null, options: IRestfulOptions = {}): Promise<T> => {
    const { path: Path } = options
    const path = id ? `${Path ?? ''}/${id}` : (Path ?? '')
    return this.handleRequest<T>({
      path: this.path + path,
      method: 'DELETE',
    })
  }
 
  protected onResponse<T>(response: AxiosResponse): T {
    return response.data as T
  }
 
  private async handleRequest<T>(config: IRestfulAPIConfig, payload = {}): Promise<T> {
    switch (config.method) {
      case 'GET':
        return this.onResponse(await this.axiosClient.get(config.path, { params: payload }))
      case 'POST':
        return this.onResponse(await this.axiosClient.post(config.path, payload))
      case 'PUT':
        return this.onResponse(await this.axiosClient.put(config.path, payload))
      case 'PATCH':
        return this.onResponse(await this.axiosClient.patch(config.path, payload))
      case 'DELETE':
        return this.onResponse(await this.axiosClient.delete(config.path, payload))
      default:
        return this.onResponse(await this.axiosClient.get(config.path, { params: payload }))
    }
  }
 
  private optionsParser(options: IRestfulOptions): IRestfulOptions {
    if (!options.path) {
      options.path = this.path
    }
    return options
  }
}
 

Usage

// userAPI.ts 
import { RestfulAPI } from './restfulAPI' 
 
class UserAPI extends RestfulAPI {
  constructor() {
    super()
    this.path = '/users'
  }
  // this will be appended to the base path '/users' and request to http://localhost:3000/users
  // with Method GET
  getUsers = () => this.getRequest({}, {
    path: '', 
  })
}
 
const userAPI = new UserAPI()
export default userAPI