Skip to main content

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: "..." });