Files

37 lines
1.4 KiB
TypeScript

/**
* The answer of a validate endpoint: problems maps a field of the validated value to a message; message describes what
* the value would break elsewhere, or is empty.
*/
export type Validation = {
problems: Record<string, string>
message: string
}
/** A failed call: the server's message and status, with the problems and message of a refused write (422). */
export class ApiError extends Error {
status: number
problems: Record<string, string>
detail: string
constructor(message: string, status: number, problems: Record<string, string> = {}, detail = '') {
super(message)
this.status = status
this.problems = problems
this.detail = detail
}
}
/** Calls the backend and returns the decoded JSON; a failed call throws ApiError with the backend's message. */
export async function request<T>(method: string, path: string, body?: unknown): Promise<T> {
const response = await fetch(path, {
method,
headers: body === undefined ? {} : { 'Content-Type': 'application/json' },
body: body === undefined ? undefined : JSON.stringify(body),
})
if (!response.ok) {
const payload = (await response.json().catch(() => null)) as { error?: string; problems?: Record<string, string>; message?: string } | null
throw new ApiError(payload?.error ?? `${method} ${path} failed (${response.status}).`, response.status, payload?.problems, payload?.message)
}
return (await response.json()) as T
}