first commit
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
import axios from 'axios';
|
||||
import type { APIEnvelope } from '../lib/types';
|
||||
import { useAuthStore } from '../stores/authStore';
|
||||
|
||||
/**
|
||||
* Typed error class for API errors, providing structured error handling
|
||||
* across the application.
|
||||
*/
|
||||
export class ApiError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = 'ApiError';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Central Axios instance configured for the Stock Data Backend API.
|
||||
* - Base URL: /api/v1/
|
||||
* - Timeout: 30 seconds
|
||||
* - JSON content type
|
||||
*/
|
||||
const apiClient = axios.create({
|
||||
baseURL: '/api/v1/',
|
||||
timeout: 30_000,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
|
||||
/**
|
||||
* Request interceptor: attaches JWT Bearer token from the auth store
|
||||
* to every outgoing request when a token is available.
|
||||
*/
|
||||
apiClient.interceptors.request.use((config) => {
|
||||
const token = useAuthStore.getState().token;
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
});
|
||||
|
||||
/**
|
||||
* Response interceptor:
|
||||
* - Success path: unwraps the { status, data, error } envelope, returning
|
||||
* only the `data` field. Throws ApiError if envelope status is 'error'.
|
||||
* - Error path: handles 401 by clearing auth and redirecting to login.
|
||||
* All other errors are wrapped in ApiError with a descriptive message.
|
||||
*/
|
||||
apiClient.interceptors.response.use(
|
||||
(response) => {
|
||||
const envelope = response.data as APIEnvelope;
|
||||
if (envelope.status === 'error') {
|
||||
throw new ApiError(envelope.error ?? 'Unknown API error');
|
||||
}
|
||||
// Return unwrapped data — callers receive the inner payload directly.
|
||||
// We override the response shape here; downstream API functions cast as needed.
|
||||
response.data = envelope.data;
|
||||
return response;
|
||||
},
|
||||
(error) => {
|
||||
if (axios.isAxiosError(error) && error.response?.status === 401) {
|
||||
useAuthStore.getState().logout();
|
||||
window.location.href = '/login';
|
||||
}
|
||||
const msg =
|
||||
error.response?.data?.error ?? error.message ?? 'Network error';
|
||||
throw new ApiError(msg);
|
||||
},
|
||||
);
|
||||
|
||||
export default apiClient;
|
||||
Reference in New Issue
Block a user