Frontend Development 13 min read

A Comprehensive Guide to Building a Flexible Axios Wrapper with Interceptors, Request Cancellation, and TypeScript in Vue3

This article demonstrates how to create a reusable Axios wrapper for Vue3 projects, covering basic class‑based encapsulation, flexible global and instance interceptors, custom request configuration types, request cancellation management, and practical testing examples, all written in TypeScript.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
A Comprehensive Guide to Building a Flexible Axios Wrapper with Interceptors, Request Cancellation, and TypeScript in Vue3

Although the Fetch API is widely adopted, many legacy browsers still lack support and Axios continues to receive millions of weekly downloads, indicating its indispensable role. This guide walks through building a flexible, reusable Axios wrapper that provides ubiquitous code hints, adaptable interceptors, multiple instances, per‑instance configuration, and request cancellation.

Basic Wrapper

The core starts with a simple class that creates an Axios instance and exposes a request method:

// index.ts
import axios from 'axios'
import type { AxiosInstance, AxiosRequestConfig } from 'axios'

class Request {
  // axios instance
  instance: AxiosInstance

  constructor(config: AxiosRequestConfig) {
    this.instance = axios.create(config)
  }
  request(config: AxiosRequestConfig) {
    return this.instance.request(config)
  }
}
export default Request

Using a class instead of a function allows creation of multiple instances, enhancing reusability and encapsulation.

Interceptor Wrapping

Three interceptor levels are defined: class interceptors, instance interceptors, and per‑API interceptors. The class interceptor attaches global request/response handlers:

// index.ts (class interceptor)
this.instance.interceptors.request.use(
  (res: AxiosRequestConfig) => { console.log('Global request interceptor'); return res },
  (err: any) => err,
)
this.instance.interceptors.response.use(
  (res: AxiosResponse) => { console.log('Global response interceptor'); return res.data },
  (err: any) => err,
)

Response data is automatically unwrapped from .data because most APIs return payloads under that property.

Instance Interceptor

To allow per‑instance customization, a RequestInterceptors interface is introduced and merged into a new RequestConfig type that extends AxiosRequestConfig :

// types.ts
export interface RequestInterceptors {
  // request interceptor
  requestInterceptors?: (config: AxiosRequestConfig) => AxiosRequestConfig
  requestInterceptorsCatch?: (err: any) => any
  // response interceptor
  responseInterceptors?:
(config: T) => T
  responseInterceptorsCatch?: (err: any) => any
}
export interface RequestConfig extends AxiosRequestConfig {
  interceptors?: RequestInterceptors
}

The constructor stores the provided interceptors and registers them after the global ones, ensuring the execution order: instance request → class request → instance response → class response.

API‑Level Interceptor

When calling request() , the method checks if the supplied RequestConfig contains its own interceptors and applies them before sending the request and after receiving the response.

Request Method Wrapping

A helper ywzRequest is exported to simplify API calls. It defaults to GET, automatically maps data to params for GET requests, and returns a typed Promise :

// src/server/index.ts
interface YWZRequestConfig
extends RequestConfig { data?: D }
interface YWZResponse
{ code: number; message: string; data: T }

const ywzRequest =
(config: YWZRequestConfig
) => {
  const { method = 'GET' } = config
  if (method === 'GET' || method === 'get') {
    config.params = config.data
  }
  return request.request
>(config)
}
export default ywzRequest

Request Cancellation

To prevent duplicate or unwanted requests, the wrapper maintains two collections: requestUrlList and cancelRequestSourceList . When a request is created, its URL and a cancel token are stored; after the request settles, both entries are removed.

// index.ts (cancellation setup)
request
(config: RequestConfig): Promise
{
  if (config.interceptors?.requestInterceptors) {
    config = config.interceptors.requestInterceptors(config)
  }
  const url = config.url
  if (url) {
    this.requestUrlList?.push(url)
    config.cancelToken = new axios.CancelToken(c => {
      this.cancelRequestSourceList?.push({ [url]: c })
    })
  }
  return this.instance.request
(config)
    .then(res => {
      if (config.interceptors?.responseInterceptors) {
        res = config.interceptors.responseInterceptors
(res)
      }
      return res
    })
    .finally(() => { url && this.delUrl(url) })
}

Utility methods getSourceIndex , delUrl , cancelRequest , and cancelAllRequest provide fine‑grained control over single or multiple pending requests.

Testing

A sample Vue component demonstrates how to call the wrapped request, apply per‑API interceptors, and trigger cancellation via UI buttons. The console output shows the order of interceptor execution and confirms that cancellation works as expected.

Conclusion

The article ends with a call for feedback and provides the GitHub repository link for the full project. Readers are encouraged to star, fork, or contribute to the code.

FrontendTypeScriptAxiosHTTPrequest-cancellationVue3Interceptors
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.