A couple of days ago in my current working project, we decided to rewrite our simple fetch service to a more powerful solution with the ability to cancel requests which were sent previously. From the beginning, we decided to use library and , as everybody has experience with it previously, and it provides a solution for request canceling based on Promises. It’s already working but will be extended in nearly future. axios TypeScript So, let’s start. Firstly, we need to create an Axios service. All our services in the current project is a class-based, so we need to create a class . There are 2 base approaches of how we can use Axios: directly use object from import or create a new instance with . We will use the last one. Service will have 2 fields: instance which has a type and which has a type : AxiosService axios axios.create AxiosInstance cancelToken CancelTokenStatic class AxiosService { instance: AxiosInstance; cancelTokenStatic: CancelTokenStatic; } Then in the constructor, we create : axiosInstanse constructor(url: string) { this.instance = axios.create({ headers: { Accept: 'application/json', 'Content-Type': 'application/json', }, baseURL: url }); this.cancelTokenStatic = axios.CancelToken; } function can take an object with options. In our case, we set field and told that our instance will work with and provide a base URL address that will be used for every request. provide cancel requests logic, so we also save it axios.create headers json axios.CancelToken One more thing that we need is to create a public function that will update . We need it for some specific business cases: baseURL setBaseURL(url: string): void { this.instance.defaults.baseURL = url; } Full version of class: AxiosService import axios, { AxiosInstance, CancelTokenStatic } from 'axios'; class AxiosService { instance: AxiosInstance; cancelTokenStatic: CancelTokenStatic; constructor(url: string) { this.instance = axios.create({ transformRequest: [ (data, headers): string => { headers['Authorization'] = `Bearer ${authService.getToken()}`; return JSON.stringify(data); }, ], headers: { Accept: 'application/json', 'Content-Type': 'application/json', }, baseURL: url, }); this.cancelTokenStatic = axios.CancelToken; } setBaseURL(url: string): void { this.instance.defaults.baseURL = url; } } The next step is to create which will implement all other logic with requests. Why do we need to create a new service instead of just adding request logic to AxiosService? Because it will help us to use any other rest library instead of Axios if we want to do this in the future. HttpService HttpService will have 3 fields: class HttpService { private axiosInstance: AxiosInstance; private sourceMap: { [key: string]: CancelTokenSource[] } = {}; private cancelToken: CancelTokenStatic; } and we will set in constructor, will be our store where we will keep cancel tokens for requests axiosInstance cancelToken sourceMap constructor(instance: AxiosInstance, cancelToken: CancelTokenStatic) { this.axiosInstance = instance; this.cancelToken = cancelToken; } Now let’s implement the logic for canceling requests. private createCancelToken = (cancelKey: string): CancelToken => { const source = this.cancelToken.source(); if (this.sourceMap[cancelKey]) { this.sourceMap[cancelKey].push(source); } else { this.sourceMap[cancelKey] = [source]; } return source.token; }; Private function firstly create a new cancel token source. Then we check if our already have a field with key , then we just add new to array or we create a new field in and the source is the first element in the array. createCancelToken sourceMap cancelKey cancelSource sourceMap The next step is to create method. cancelPreviousRequests cancelPreviousRequests = (cancelKey: string): void => { const requests = this.sourceMap[cancelKey] || []; requests.forEach((item) => { item.cancel('cancel'); }); this.sourceMap[cancelKey] = []; }; We take a from the argument and find such field in . Then we iterate through every in it and call the function. And then delete the target field in via saving an empty array to it. cancelKey sourceMap CancelTokenSource cancel sourceMap Now we can start with requests logic. We will use the general method for all requests. This method will take some options. Let’s define interface for them: sendRequest interface RequestOptions { url: string; // url for request method?: Method; // HTTP method params?: Dictionary<any> | string; // request params cancelKey?: string; // key for sourceMap if request can be canceled responseType?: ResponseType; // type for response } And the method which will return Promise: sendRequest sendRequest<T = unknown>({ url, method = 'GET', params, cancelKey, responseType, }: RequestOptions): Promise<AxiosResponse<T> | void> { let cancelToken; if (cancelKey) { cancelToken = this.createCancelToken(cancelKey); } switch (method) { case 'POST': return this.postRequest({ url, data: params, cancelToken }); default: return this.getRequest({ url, cancelToken, responseType, }); } } We use generic for returned type for response and method we set to T by default. Firstly, we check if we have a , then we need to create and push it to . Then we use switch construction to iterate through the request’s methods and call corresponding functions. GE cancelKey cancelToken sourceMap The request function also will take some params, so we need to define an interface for them: type RequestParams = { url: string; cancelToken?: CancelToken; data?: Dictionary<any> | string; }; We have 2 base HTTP methods for POST and GET requests: private async postRequest({ url, data, cancelToken, }: RequestParams): Promise<AxiosResponse | void> { try { return await this.axiosInstance.post(url, data, { cancelToken }); } catch (error) { return Promise.reject(error); } } private async getRequest({ url, cancelToken, }: RequestParams): Promise<AxiosResponse | void> { try { return await this.axiosInstance.get(url, { cancelToken }); } catch (error) { return Promise.reject(error); } } Nothing special here. We just call the corresponding request from with our params axiosInstance Full code for HttpService: import { AxiosResponse, Method, CancelTokenSource, CancelToken, AxiosInstance, CancelTokenStatic, } from 'axios'; import { axiosService } from './AxiosService'; interface RequestOptions { url: string; method?: Method; params?: Dictionary<any> | string; cancelKey?: string; } interface RequestParams { url: string; cancelToken?: CancelToken; data?: Dictionary<any> | string; } class HttpService { private axiosInstance: AxiosInstance; private sourceMap: { [key: string]: CancelTokenSource[] } = {}; private cancelToken: CancelTokenStatic; constructor(instance: AxiosInstance, cancelToken: CancelTokenStatic) { this.axiosInstance = instance; this.cancelToken = cancelToken; } private createCancelToken = (cancelKey: string): CancelToken => { const source = this.cancelToken.source(); if (this.sourceMap[cancelKey]) { this.sourceMap[cancelKey].push(source); } else { this.sourceMap[cancelKey] = [source]; } return source.token; }; cancelPreviousRequests = (cancelKey: string): void => { const requests = this.sourceMap[cancelKey] || []; requests.forEach((item) => { item.cancel('cancel'); }); this.sourceMap[cancelKey] = []; }; private async postRequest({ url, data, cancelToken, }: RequestParams): Promise<AxiosResponse | void> { try { return await this.axiosInstance.post(url, data, { cancelToken }); } catch (error) { return Promise.reject(error); } } private async getRequest({ url, cancelToken }: RequestParams): Promise<AxiosResponse | void> { try { return await this.axiosInstance.get(url, { cancelToken }); } catch (error) { return Promise.reject(error); } } sendRequest<T = unknown>({ url, method = 'GET', params, cancelKey, }: RequestOptions): Promise<AxiosResponse<T> | void> { let cancelToken; if (cancelKey) { cancelToken = this.createCancelToken(cancelKey); } switch (method) { case 'POST': return this.postRequest({ url, data: params, cancelToken }); default: return this.getRequest({ url, cancelToken, }); } } Example of usage: httpService.cancelPreviousRequests('testRequest'); httpService .sendRequest<string[]>({ url: '/api/testurl', cancelKey: 'testRequest', }) .then((response) => { if (response?.data) { console.log(response.data) } }) .catch((error) => { console.warn(error); });