Files
NexaPG/frontend/src/api.js
nessi 9aecbea68b Add consistent API error handling and documentation
Introduced standardized error response formats for API errors, including middleware for consistent request IDs and exception handlers. Updated the frontend to parse and process these error responses, and documented the error format in the README for reference.
2026-02-13 17:30:05 +01:00

59 lines
1.7 KiB
JavaScript

function resolveApiUrl() {
const raw = (import.meta.env.VITE_API_URL || "").trim();
const fallback = "/api/v1";
if (!raw) return fallback;
try {
const parsed = new URL(raw, window.location.origin);
if (window.location.protocol === "https:" && parsed.protocol === "http:") {
// Avoid mixed-content when UI is served over HTTPS.
parsed.protocol = "https:";
}
return parsed.toString().replace(/\/$/, "");
} catch {
return fallback;
}
}
const API_URL = resolveApiUrl();
export async function apiFetch(path, options = {}, tokens, onUnauthorized) {
const headers = {
"Content-Type": "application/json",
...(options.headers || {}),
};
if (tokens?.accessToken) {
headers.Authorization = `Bearer ${tokens.accessToken}`;
}
let res = await fetch(`${API_URL}${path}`, { ...options, headers });
if (res.status === 401 && tokens?.refreshToken && onUnauthorized) {
const refreshed = await onUnauthorized();
if (refreshed) {
headers.Authorization = `Bearer ${refreshed.accessToken}`;
res = await fetch(`${API_URL}${path}`, { ...options, headers });
}
}
if (!res.ok) {
const raw = await res.text();
let parsed = null;
try {
parsed = raw ? JSON.parse(raw) : null;
} catch {
parsed = null;
}
const message = parsed?.message || raw || `HTTP ${res.status}`;
const err = new Error(message);
err.status = res.status;
err.code = parsed?.code || null;
err.details = parsed?.details || null;
err.requestId = parsed?.request_id || res.headers.get("x-request-id") || null;
throw err;
}
if (res.status === 204) return null;
return res.json();
}
export { API_URL };