/** * Server-only fetch service. * Import as: import { serverFetch } from '@/lib/api/server' * * Use this in Server Components and Server Actions — never in client components. * All errors throw ApiError; callers decide whether to notFound(), redirect(), or surface an error boundary. */ import { headers } from 'next/headers'; import { API_URL } from '@/config'; import { COOKIE_NAMES } from '@/lib/cookies'; import { HEADER_NAMES } from '@/constants'; import { getServerCookie } from '@/lib/cookies/server'; import { ApiError } from './errors'; async function parseBody(response: Response): Promise<{ message?: string; code?: string }> { try { return await response.json(); } catch { return {}; } } export async function serverFetch(path: string, options?: RequestInit): Promise { const token = await getServerCookie(COOKIE_NAMES.ACCESS_TOKEN); let locale = 'fa'; try { const reqHeaders = await headers(); locale = reqHeaders.get(HEADER_NAMES.LOCALE) ?? 'fa'; } catch { // Outside request context (build-time prerendering) — use default locale } const reqHeaders: Record = { 'Content-Type': 'application/json', 'Accept-Language': locale, }; if (token) { reqHeaders['Authorization'] = `Bearer ${token}`; } let response: Response; try { response = await fetch(`${API_URL}${path}`, { cache: 'no-store', ...options, headers: { ...reqHeaders, ...(options?.headers as Record) }, }); } catch { throw new ApiError(0, 'Network error'); } if (response.ok) { if (response.status === 204) return undefined as T; return response.json() as Promise; } const body = await parseBody(response); throw new ApiError(response.status, body.message ?? response.statusText, body.code); }