API Client
File: frontend/src/api/client.ts
The app uses a centralized Axios instance for all backend communication.
Configuration
const apiClient = axios.create({
baseURL: "/api",
headers: { "Content-Type": "application/json" },
});
All requests go to /api/* which is proxied to the backend by Vite (dev) or the reverse proxy (production).
Interceptors
Request: Auth Token
Every outgoing request automatically attaches the JWT from localStorage:
apiClient.interceptors.request.use((config) => {
const token = localStorage.getItem("auth_token");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
Response: 401 Redirect
On a 401 response, the client clears the stored token and redirects to the login page:
apiClient.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
localStorage.removeItem("auth_token");
window.location.href = "/login";
}
return Promise.reject(error);
}
);
Usage
import apiClient from "../api/client";
// GET
const { data } = await apiClient.get("/issues/kpis?days=7");
// POST
const { data } = await apiClient.post("/chat", { message: "..." });