/** * Minimal fetch wrapper for the EDEN API. Base URL comes from * `VITE_API_BASE_URL` (set in apps/core-web/.env — see .env.example), * falling back to the documented local dev default so `pnpm dev` works * out of the box against `docker compose up eden-postgres` + a locally * run `apps/api`. */ const DEFAULT_BASE_URL = "http://localhost:8080/api/v1"; export const API_BASE_URL = (import.meta.env.VITE_API_BASE_URL as string | undefined) ?? DEFAULT_BASE_URL; export class ApiError extends Error { readonly status: number; constructor(status: number, message: string) { super(message); this.name = "ApiError"; this.status = status; } } interface ApiErrorBody { message?: string | string[]; error?: string; statusCode?: number; } function extractErrorMessage(body: unknown, fallback: string): string { const parsed = body as ApiErrorBody | null; if (parsed && typeof parsed === "object") { if (Array.isArray(parsed.message)) return parsed.message.join(" "); if (typeof parsed.message === "string") return parsed.message; if (typeof parsed.error === "string") return parsed.error; } return fallback; } export interface RequestOptions { accessToken?: string; } export async function apiRequest( path: string, init?: RequestInit, options?: RequestOptions, ): Promise { let response: Response; try { response = await fetch(`${API_BASE_URL}${path}`, { ...init, headers: { "Content-Type": "application/json", ...(options?.accessToken ? { Authorization: `Bearer ${options.accessToken}` } : {}), ...init?.headers, }, }); } catch { throw new ApiError(0, "Não foi possível conectar à API. Verifique sua conexão e tente novamente."); } if (response.status === 204) { return undefined as TResponse; } const text = await response.text(); const body = text ? JSON.parse(text) : undefined; if (!response.ok) { throw new ApiError(response.status, extractErrorMessage(body, "Ocorreu um erro inesperado.")); } return body as TResponse; }