UNPKG

logicloom-nextjs-starter

Version:

A production-ready Next.js starter template with authentication, i18n, dark mode, and modern patterns

157 lines (138 loc) 4.43 kB
import axios from "axios"; import { storage } from "./storage"; const axiosInstance = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_URL || "/api", timeout: 10000, headers: { "Content-Type": "application/json", }, withCredentials: true, }); let isRefreshing = false; let failedQueue: Array<{ resolve: (value?: any) => void; reject: (reason?: any) => void; }> = []; const processQueue = (error: any, token: string | null = null) => { failedQueue.forEach((prom) => { if (error) { prom.reject(error); } else { prom.resolve(token); } }); failedQueue = []; }; // Request interceptor axiosInstance.interceptors.request.use( (config) => { const token = storage.getToken(); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }, (error) => { return Promise.reject(error); } ); // Response interceptor axiosInstance.interceptors.response.use( (response) => { return response; }, async (error) => { const originalRequest = error.config; // Handle 401 Unauthorized if (error.response?.status === 401 && !originalRequest._retry) { if (isRefreshing) { // If already refreshing, queue this request return new Promise((resolve, reject) => { failedQueue.push({ resolve, reject }); }) .then((token) => { originalRequest.headers.Authorization = `Bearer ${token}`; return axiosInstance(originalRequest); }) .catch((err) => { return Promise.reject(err); }); } originalRequest._retry = true; isRefreshing = true; try { // Try to refresh token const response = await fetch("/api/auth/refresh", { method: "POST", credentials: "include", // Important: send cookies }); if (response.ok) { const data = await response.json(); const newToken = data.token; // Update token in storage storage.setToken(newToken); // Process queued requests processQueue(null, newToken); // Retry original request originalRequest.headers.Authorization = `Bearer ${newToken}`; return axiosInstance(originalRequest); } else { // Refresh failed, logout user processQueue(new Error("Token refresh failed"), null); storage.clearAuth(); if (typeof window !== "undefined") { window.location.href = "/auth"; } return Promise.reject(error); } } catch (refreshError) { processQueue(refreshError, null); storage.clearAuth(); if (typeof window !== "undefined") { window.location.href = "/auth"; } return Promise.reject(refreshError); } finally { isRefreshing = false; } } // Handle other errors with detailed information if (error.response) { // Server responded with error const status = error.response.status; const data = error.response.data; const message = data?.message || `Request failed with status ${status}`; try { console.error("API Error:", { url: error.config?.url || "unknown", method: error.config?.method || "unknown", status, message, data: data ? JSON.stringify(data) : "no data", }); } catch (logError) { console.error("API Error occurred, but could not log details"); } return Promise.reject(error); } else if (error.request) { // Request made but no response try { console.error("Network Error:", { url: error.config?.url || "unknown", method: error.config?.method || "unknown", message: "No response from server", }); } catch (logError) { console.error("Network Error occurred"); } return Promise.reject( new Error("No response from server. Please check your connection.") ); } else { // Something else happened console.error("Request Error:", error.message || "Unknown error"); return Promise.reject(error); } } ); export default axiosInstance;