SPB Git forge
15commits 1branches 0releases
29.7 MBsize
maindefault branch
10 days agolast push
TypeScript 36.3% Python 31.8% Go 18% JavaScript 9.8% Shell 1.9% SQL 1.4% CSS 0.5%
1.4 KB · 41 lines typescript
Raw Blame History
1import 'server-only';2import { notFound } from 'next/navigation';34/**5 * Server-side API client. Server components talk to the FastAPI service directly (internal URL), never through the6 * browser rewrite. Every call is `no-store`: pages are dynamic and render the live state at request time.7 */8const BASE = process.env.API_URL_INTERNAL ?? process.env.API_URL ?? 'http://127.0.0.1:8352';910export class ApiError extends Error {11  constructor(12    public status: number,13    public path: string,14  ) {15    super(`API ${status} for ${path}`);16  }17}1819export async function apiGet<T>(path: string, init?: { timeoutMs?: number }): Promise<T> {20  const ctrl = new AbortController();21  const timer = setTimeout(() => ctrl.abort(), init?.timeoutMs ?? 8000);22  try {23    const res = await fetch(BASE + path, { cache: 'no-store', signal: ctrl.signal, headers: { accept: 'application/json' } });24    if (res.status === 404) notFound();25    if (!res.ok) throw new ApiError(res.status, path);26    return (await res.json()) as T;27  } finally {28    clearTimeout(timer);29  }30}3132/** Like apiGet but returns null instead of throwing when the API is unreachable/erroring (used for optional panels). */33export async function apiTry<T>(path: string): Promise<T | null> {34  try {35    return await apiGet<T>(path);36  } catch (e) {37    if (e && typeof e === 'object' && 'digest' in e && String((e as { digest?: string }).digest).startsWith('NEXT_HTTP_ERROR_FALLBACK')) throw e;38    return null;39  }40}41