55 lines
1.4 KiB
TypeScript
55 lines
1.4 KiB
TypeScript
const apiBaseUrl = import.meta.env.VITE_API_BASE_URL ?? ''
|
|
|
|
export class ApiError extends Error {
|
|
readonly status: number
|
|
readonly details: unknown
|
|
|
|
constructor(message: string, status: number, details: unknown) {
|
|
super(message)
|
|
this.name = 'ApiError'
|
|
this.status = status
|
|
this.details = details
|
|
}
|
|
}
|
|
|
|
export async function getJson<T>(path: string): Promise<T> {
|
|
return requestJson<T>(path, {
|
|
method: 'GET',
|
|
})
|
|
}
|
|
|
|
export async function sendJson<T>(path: string, method: 'POST' | 'PUT', body?: unknown): Promise<T> {
|
|
return requestJson<T>(path, {
|
|
method,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
})
|
|
}
|
|
|
|
async function requestJson<T>(path: string, init: RequestInit): Promise<T> {
|
|
const response = await fetch(`${apiBaseUrl}${path}`, {
|
|
...init,
|
|
headers: {
|
|
Accept: 'application/json',
|
|
...init.headers,
|
|
},
|
|
})
|
|
|
|
const payload = await readResponsePayload(response)
|
|
if (!response.ok) {
|
|
throw new ApiError(`Request failed with status ${response.status}`, response.status, payload)
|
|
}
|
|
|
|
return payload as T
|
|
}
|
|
|
|
async function readResponsePayload(response: Response): Promise<unknown> {
|
|
const contentType = response.headers.get('content-type') ?? ''
|
|
if (!contentType.includes('application/json')) {
|
|
return response.text()
|
|
}
|
|
return response.json()
|
|
}
|