Files
juwan-frontend/lib/api/http.ts
T
2026-02-28 12:17:42 +08:00

59 lines
1.3 KiB
TypeScript

import { isApiError, type ApiError } from "@/lib/errors"
type JsonRequestInit = Omit<RequestInit, "body" | "headers"> & {
headers?: HeadersInit
body?: BodyInit | null
json?: unknown
}
async function readJsonBody(res: Response): Promise<unknown | null> {
const text = await res.text()
if (!text) return null
try {
return JSON.parse(text) as unknown
} catch {
return null
}
}
export async function httpJson<T>(path: string, init?: JsonRequestInit): Promise<T> {
if (/^https?:\/\//.test(path)) {
throw new Error("Absolute URLs are not allowed")
}
const { json, headers: headersInit, body: bodyInit, ...rest } = init ?? {}
const headers = new Headers(headersInit)
headers.set("Accept", "application/json")
const body = json === undefined ? bodyInit : JSON.stringify(json)
if (json !== undefined && !headers.has("Content-Type")) {
headers.set("Content-Type", "application/json")
}
const res = await fetch(path, {
...rest,
headers,
body,
})
const data = await readJsonBody(res)
if (res.ok) {
return data as T
}
const apiError = isApiError(data) ? data : null
if (res.status === 401 || apiError?.code === 401) {
throw new Error("UNAUTHORIZED")
}
if (apiError) {
throw apiError
}
throw { code: res.status, msg: res.statusText || "Request failed" } satisfies ApiError
}