Skip to main content

API Client & Networking

All network requests in the application pass through the centralized Axios client defined in src/api/apiClient.ts.


Axios Instance Configuration​

// src/api/apiClient.ts
import axios from 'axios';
import { appConfig } from '../config/runtimeConfig';
import { IMPERSONATION_STORAGE_KEY } from '../features/impersonation/types/impersonation.types';

const apiClient = axios.create({
baseURL: appConfig.apiBaseUrl,
headers: {
'Content-Type': 'application/json',
},
});

Request Interceptor​

The request interceptor automatically enriches every outgoing HTTP request with authentication credentials and impersonation tokens:

apiClient.interceptors.request.use(
(config) => {
// 1. Attach JWT Authorization Bearer
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}

// 2. Check for active Impersonation session
const impersonationRaw = sessionStorage.getItem(IMPERSONATION_STORAGE_KEY);
if (impersonationRaw) {
try {
const target = JSON.parse(impersonationRaw) as { employeeId?: string };
if (target.employeeId) {
config.headers['X-On-Behalf-Of'] = target.employeeId;
}
} catch {
// ignore malformed storage
}
}

return config;
},
(error) => Promise.reject(error)
);

Response Interceptor & 401 Handling​

apiClient.interceptors.response.use(
(response) => response,
(error) => {
if (
error.response?.status === 401 &&
!window.location.pathname.startsWith('/login') &&
!window.location.pathname.startsWith('/auth/callback')
) {
localStorage.removeItem('token');
sessionStorage.clear();
window.location.href = '/login';
}
return Promise.reject(error);
}
);

Error Helper: getApiErrorMessage.ts​

To safely handle unknown backend error payloads, use getApiErrorMessage():

// shared/utils/getApiErrorMessage.ts
import axios from 'axios';

export function getApiErrorMessage(error: unknown, fallbackMessage = 'An unexpected error occurred'): string {
if (axios.isAxiosError(error)) {
return error.response?.data?.message || error.response?.data?.error || error.message || fallbackMessage;
}
if (error instanceof Error) {
return error.message;
}
return fallbackMessage;
}

Usage Example in Components​

import { getApiErrorMessage } from '../../shared/utils/getApiErrorMessage';
import { toast } from 'sonner';

try {
await createEmployeeMutation.mutateAsync(formData);
toast.success('Employee created successfully');
} catch (error) {
toast.error(getApiErrorMessage(error, 'Failed to create employee'));
}