diff --git a/src/lib/admin/api.ts b/src/lib/admin/api.ts
new file mode 100644
index 0000000..5424034
--- /dev/null
+++ b/src/lib/admin/api.ts
@@ -0,0 +1,93 @@
+import { goto } from '$app/navigation'
+
+export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
+
+export interface RequestOptions
{
+ method?: HttpMethod
+ body?: TBody
+ signal?: AbortSignal
+ headers?: Record
+}
+
+export interface ApiError extends Error {
+ status: number
+ details?: unknown
+}
+
+function getAuthHeader() {
+ if (typeof localStorage === 'undefined') return {}
+ const auth = localStorage.getItem('admin_auth')
+ return auth ? { Authorization: `Basic ${auth}` } : {}
+}
+
+async function handleResponse(res: Response) {
+ if (res.status === 401) {
+ // Redirect to login for unauthorized requests
+ try {
+ goto('/admin/login')
+ } catch {}
+ }
+
+ const contentType = res.headers.get('content-type') || ''
+ const isJson = contentType.includes('application/json')
+ const data = isJson ? await res.json().catch(() => undefined) : undefined
+
+ if (!res.ok) {
+ const err: ApiError = Object.assign(new Error('Request failed'), {
+ status: res.status,
+ details: data
+ })
+ throw err
+ }
+ return data
+}
+
+export async function request(
+ url: string,
+ opts: RequestOptions = {}
+): Promise {
+ const { method = 'GET', body, signal, headers } = opts
+
+ const isFormData = typeof FormData !== 'undefined' && body instanceof FormData
+ const mergedHeaders: Record = {
+ ...(isFormData ? {} : { 'Content-Type': 'application/json' }),
+ ...getAuthHeader(),
+ ...(headers || {})
+ }
+
+ const res = await fetch(url, {
+ method,
+ headers: mergedHeaders,
+ body: body ? (isFormData ? (body as any) : JSON.stringify(body)) : undefined,
+ signal
+ })
+
+ return handleResponse(res) as Promise
+}
+
+export const api = {
+ get: (url: string, opts: Omit = {}) =>
+ request(url, { ...opts, method: 'GET' }),
+ post: (url: string, body: B, opts: Omit, 'method' | 'body'> = {}) =>
+ request(url, { ...opts, method: 'POST', body }),
+ put: (url: string, body: B, opts: Omit, 'method' | 'body'> = {}) =>
+ request(url, { ...opts, method: 'PUT', body }),
+ patch: (url: string, body: B, opts: Omit, 'method' | 'body'> = {}) =>
+ request(url, { ...opts, method: 'PATCH', body }),
+ delete: (url: string, opts: Omit = {}) =>
+ request(url, { ...opts, method: 'DELETE' })
+}
+
+export function createAbortable() {
+ let controller: AbortController | null = null
+ return {
+ nextSignal() {
+ if (controller) controller.abort()
+ controller = new AbortController()
+ return controller.signal
+ },
+ abort() {
+ if (controller) controller.abort()
+ }
+ }
+}