another step constructing base project

This commit is contained in:
hamid
2026-06-18 01:19:23 +03:30
parent 5388bea320
commit e135b0b919
27 changed files with 1022 additions and 355 deletions
+60
View File
@@ -0,0 +1,60 @@
/**
* 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 { 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<T>(path: string, options?: RequestInit): Promise<T> {
const token = await getServerCookie(COOKIE_NAMES.ACCESS_TOKEN);
let locale = 'fa';
try {
const reqHeaders = await headers();
locale = reqHeaders.get('x-next-intl-locale') ?? 'fa';
} catch {
// Outside request context (build-time prerendering) — use default locale
}
const reqHeaders: Record<string, string> = {
'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<string, string>) },
});
} catch {
throw new ApiError(0, 'Network error');
}
if (response.ok) {
if (response.status === 204) return undefined as T;
return response.json() as Promise<T>;
}
const body = await parseBody(response);
throw new ApiError(response.status, body.message ?? response.statusText, body.code);
}