/** * API Client * * Axios-based API client with authentication support */ import axios, { AxiosInstance, AxiosRequestConfig, AxiosError } from 'axios'; const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000'; export class ApiClient { private client: AxiosInstance; constructor() { this.client = axios.create({ baseURL: API_BASE_URL, timeout: 30000, headers: { 'Content-Type': 'application/json', }, }); // Request interceptor to add auth token this.client.interceptors.request.use( config => { const token = this.getAccessToken(); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }, error => Promise.reject(error) ); // Response interceptor to handle token refresh this.client.interceptors.response.use( response => response, async (error: AxiosError) => { const originalRequest = error.config as AxiosRequestConfig & { _retry?: boolean; }; // If 401 and not already retried, try to refresh token if (error.response?.status === 401 && !originalRequest._retry) { originalRequest._retry = true; try { const refreshToken = this.getRefreshToken(); if (refreshToken) { const { data } = await axios.post(`${API_BASE_URL}/api/v1/auth/refresh`, { refreshToken, }); this.setAccessToken(data.accessToken); // Retry original request with new token if (originalRequest.headers) { originalRequest.headers.Authorization = `Bearer ${data.accessToken}`; } return this.client(originalRequest); } } catch (refreshError) { // Refresh failed, clear tokens and redirect to login this.clearTokens(); if (typeof window !== 'undefined') { window.location.href = '/login'; } return Promise.reject(refreshError); } } return Promise.reject(error); } ); } private getAccessToken(): string | null { if (typeof window === 'undefined') return null; return localStorage.getItem('accessToken'); } private getRefreshToken(): string | null { if (typeof window === 'undefined') return null; return localStorage.getItem('refreshToken'); } private setAccessToken(token: string): void { if (typeof window !== 'undefined') { localStorage.setItem('accessToken', token); } } private clearTokens(): void { if (typeof window !== 'undefined') { localStorage.removeItem('accessToken'); localStorage.removeItem('refreshToken'); } } async get(url: string, config?: AxiosRequestConfig): Promise { const response = await this.client.get(url, config); return response.data; } async post(url: string, data?: any, config?: AxiosRequestConfig): Promise { const response = await this.client.post(url, data, config); return response.data; } async put(url: string, data?: any, config?: AxiosRequestConfig): Promise { const response = await this.client.put(url, data, config); return response.data; } async patch(url: string, data?: any, config?: AxiosRequestConfig): Promise { const response = await this.client.patch(url, data, config); return response.data; } async delete(url: string, config?: AxiosRequestConfig): Promise { const response = await this.client.delete(url, config); return response.data; } } export const apiClient = new ApiClient();