Files
Job-Info/components/api-client/api-client.ts
T
aewing 05f8e2e3b6 Add ModeTemplate with Svelte 5 frontend and components
- Create reusable components: auth, api-client, shared-types
- ModeTemplate with Hono backend and SvelteKit frontend
- Auth store refactored to class-based Svelte 5 pattern
- SSR-safe hydrate() pattern for localStorage access
- Frontend builds successfully with Svelte 5 runes
2026-01-21 05:12:25 +00:00

98 lines
2.3 KiB
TypeScript

/**
* API Client Component
*
* Frontend helper for making authenticated requests to the backend.
* Uses PocketBase token from auth session.
*/
// Header constants (inline to avoid cross-component dependency)
const HEADERS = {
AUTH: 'Authorization',
CONTENT_TYPE: 'Content-Type',
} as const;
const CONTENT_TYPES = {
JSON: 'application/json',
} as const;
function authHeader(token: string): string {
return `Bearer ${token}`;
}
export interface ApiResponse<T = unknown> {
ok: boolean;
status: number;
data?: T;
error?: string;
}
export interface ApiClientConfig {
baseUrl: string;
getToken: () => string | null;
}
/**
* Create an API client instance
*/
export function createApiClient(config: ApiClientConfig) {
const { baseUrl, getToken } = config;
async function request<T>(
method: string,
path: string,
body?: unknown
): Promise<ApiResponse<T>> {
const token = getToken();
const headers: Record<string, string> = {
[HEADERS.CONTENT_TYPE]: CONTENT_TYPES.JSON,
};
if (token) {
headers[HEADERS.AUTH] = authHeader(token);
}
try {
const response = await fetch(`${baseUrl}${path}`, {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
});
const data = response.headers.get('content-type')?.includes('application/json')
? await response.json()
: await response.text();
if (!response.ok) {
return {
ok: false,
status: response.status,
error: typeof data === 'string' ? data : data.message || 'Request failed',
};
}
return {
ok: true,
status: response.status,
data: data as T,
};
} catch (err) {
return {
ok: false,
status: 0,
error: err instanceof Error ? err.message : 'Network error',
};
}
}
return {
get: <T>(path: string) => request<T>('GET', path),
post: <T>(path: string, body?: unknown) => request<T>('POST', path, body),
put: <T>(path: string, body?: unknown) => request<T>('PUT', path, body),
patch: <T>(path: string, body?: unknown) => request<T>('PATCH', path, body),
delete: <T>(path: string) => request<T>('DELETE', path),
};
}
export type ApiClient = ReturnType<typeof createApiClient>;