Files
crank/apps/ui/src/shared/api/client.ts
T
2026-03-25 19:27:31 +03:00

80 lines
1.8 KiB
TypeScript

export class ApiError extends Error {
readonly status: number;
constructor(message: string, status: number) {
super(message);
this.name = "ApiError";
this.status = status;
}
}
type RequestInitWithJson = RequestInit & {
json?: unknown;
};
async function request<TResponse>(
input: string,
init: RequestInitWithJson = {},
): Promise<TResponse> {
const headers = new Headers(init.headers);
const body =
init.json === undefined ? init.body : JSON.stringify(init.json, null, 2);
if (init.json !== undefined) {
headers.set("Content-Type", "application/json");
}
const response = await fetch(input, {
...init,
headers,
body,
});
if (!response.ok) {
let message = `${response.status} ${response.statusText}`;
try {
const errorPayload = (await response.json()) as {
error?: { message?: string };
};
message = errorPayload.error?.message ?? message;
} catch {
message = `${response.status} ${response.statusText}`;
}
throw new ApiError(message, response.status);
}
const contentType = response.headers.get("Content-Type") ?? "";
if (contentType.includes("application/yaml")) {
return (await response.text()) as TResponse;
}
return (await response.json()) as TResponse;
}
export function getJson<TResponse>(input: string) {
return request<TResponse>(input);
}
export function getText(input: string) {
return request<string>(input);
}
export function postJson<TResponse>(input: string, json?: unknown) {
return request<TResponse>(input, {
method: "POST",
json,
});
}
export function postText<TResponse>(input: string, text: string) {
return request<TResponse>(input, {
method: "POST",
headers: {
"Content-Type": "application/yaml",
},
body: text,
});
}