import { Platform } from "react-native"; import { apiLogger } from "../logging"; import { useAuthStore } from "../stores"; const API_BASE_URL = process.env.EXPO_PUBLIC_API_URL || Platform.select({ android: "http://10.0.2.2:3001/api", default: "http://localhost:3001/api", }); type HttpMethod = "GET" | "POST" | "PUT" | "DELETE"; interface RequestOptions { headers?: Record; body?: unknown; skipAuth?: boolean; } function getAuthHeaders(): Record { const user = useAuthStore.getState().user; apiLogger.debug(`getAuthHeaders - user: ${JSON.stringify(user)}`); if (user?.id) { return { "X-User-Id": user.id }; } return {}; } async function request( method: HttpMethod, endpoint: string, options?: RequestOptions, ): Promise { const start = performance.now(); apiLogger.debug(`${method} ${endpoint}`); try { const authHeaders = options?.skipAuth ? {} : getAuthHeaders(); const response = await fetch(`${API_BASE_URL}${endpoint}`, { method, headers: { "Content-Type": "application/json", ...authHeaders, ...options?.headers, }, body: options?.body ? JSON.stringify(options.body) : undefined, }); const duration = Math.round(performance.now() - start); if (!response.ok) { apiLogger.error(`${method} ${endpoint} - ${response.status} (${duration}ms)`); throw new Error(`HTTP ${response.status}`); } apiLogger.debug(`${method} ${endpoint} - ${response.status} (${duration}ms)`); const text = await response.text(); if (!text) { return undefined as T; } return JSON.parse(text); } catch (error) { const duration = Math.round(performance.now() - start); apiLogger.error(`${method} ${endpoint} failed (${duration}ms): ${error}`); throw error; } } export const ApiClient = { get: async ( endpoint: string, options?: Omit, ): Promise => { return request("GET", endpoint, options); }, post: async ( endpoint: string, body?: unknown, options?: RequestOptions, ): Promise => { return request("POST", endpoint, { ...options, body }); }, put: async ( endpoint: string, body?: unknown, options?: RequestOptions, ): Promise => { return request("PUT", endpoint, { ...options, body }); }, delete: async ( endpoint: string, options?: Omit, ): Promise => { return request("DELETE", endpoint, options); }, }; export { API_BASE_URL };