repo_description: The flexible backend for all your projects 🐰 Turn your DB into a headless CMS, admin panels, or apps with a custom UI, instant APIs, auth & more.
repo_topics: api app cms composable data-visualization database directus graphql headless-cms javascript mariadb mssql mysql no-code node postgresql sql sqlite typescript vue
code: import axios, { AxiosError, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
  import PQueue, { Options, QueueAddOptions } from 'p-queue';
  import sdk from './sdk';
  import { useRequestsStore } from '@/stores/requests';
  import { getRootPath } from '@/utils/get-root-path';
  
  const api = axios.create({
  	baseURL: getRootPath(),
  	withCredentials: true,
  });
  
  export let requestQueue: PQueue = new PQueue({
  	concurrency: 5,
  	intervalCap: 5,
  	interval: 500,
  	carryoverConcurrencyCount: true,
  });
  
  export type RequestConfig = InternalAxiosRequestConfig & { measureLatency?: boolean };
  
  type InternalRequestConfig = RequestConfig & { id: string; start?: number };
  type Response = AxiosResponse & { config: RequestConfig };
  export type RequestError = AxiosError & { response: Response };
  
  export const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
  	const requestsStore = useRequestsStore();
  	const id = requestsStore.startRequest();
  
  	const requestConfig: InternalRequestConfig = {
  		...config,
  		id,
  	};
  
  	return new Promise((resolve) => {
  		requestQueue.add(async () => {
  			// use getToken to await currently active refreshes
  			await sdk.getToken().catch(() => {
  				/* fail gracefully */
  			});
  
  			if (requestConfig.measureLatency) requestConfig.start = performance.now();
  
  			return resolve(requestConfig);
  		});
  	});
  };
  
  export const onResponse = (response: AxiosResponse | Response): AxiosResponse | Response => {
  	onRequestEnd(response);
  	return response;
  };
  
  export const onError = async (error: RequestError): Promise<RequestError> => {
  	onRequestEnd(error.response);
  	return Promise.reject(error);
  };
  
  api.interceptors.request.use(onRequest);
  api.interceptors.response.use(onResponse, onError);
  
  export default api;
  
  export function resumeQueue() {
  	if (!requestQueue.isPaused) return;
  	requestQueue.start();
  }
  
  export async function replaceQueue(options?: Options<any, QueueAddOptions>) {
  	await requestQueue.onIdle();
  	requestQueue = new PQueue(options);
  }
  
  function onRequestEnd(response?: AxiosResponse | Response) {
  	// Note: Cancelled requests don't respond with the config
  	const config = response?.config as InternalRequestConfig | undefined;
  
  	if (config?.id) {
  		const requestsStore = useRequestsStore();
  		requestsStore.endRequest(config.id);
  	}
  }
  