75 lines
1.9 KiB
TypeScript
75 lines
1.9 KiB
TypeScript
export class ApiError extends Error {
|
|
constructor(
|
|
message: string,
|
|
public readonly status: number,
|
|
public readonly code?: string,
|
|
) {
|
|
super(message);
|
|
this.name = 'ApiError';
|
|
}
|
|
}
|
|
|
|
export class NotFoundError extends ApiError {
|
|
constructor(message = 'Not found') {
|
|
super(message, 404, 'not_found');
|
|
this.name = 'NotFoundError';
|
|
}
|
|
}
|
|
|
|
export class RateLimitError extends ApiError {
|
|
constructor(message = 'Too many requests. Please wait a moment before trying again.') {
|
|
super(message, 429, 'rate_limited');
|
|
this.name = 'RateLimitError';
|
|
}
|
|
}
|
|
|
|
export class ValidationError extends ApiError {
|
|
constructor(message: string) {
|
|
super(message, 422, 'validation_error');
|
|
this.name = 'ValidationError';
|
|
}
|
|
}
|
|
|
|
export class NetworkError extends Error {
|
|
constructor(message: string, public readonly cause?: unknown) {
|
|
super(message);
|
|
this.name = 'NetworkError';
|
|
}
|
|
}
|
|
|
|
export async function apiFetch<T>(url: string, init?: RequestInit): Promise<T> {
|
|
let res: Response;
|
|
try {
|
|
res = await fetch(url, init);
|
|
} catch (err) {
|
|
throw new NetworkError(
|
|
`Network error: ${err instanceof Error ? err.message : String(err)}`,
|
|
err,
|
|
);
|
|
}
|
|
|
|
if (res.status === 404) throw new NotFoundError();
|
|
if (res.status === 429) throw new RateLimitError();
|
|
|
|
if (res.status === 422) {
|
|
let body: { error?: string; message?: string } = {};
|
|
try {
|
|
body = (await res.json()) as { error?: string; message?: string };
|
|
} catch {
|
|
// ignore
|
|
}
|
|
throw new ValidationError(body.error ?? body.message ?? 'Validation failed');
|
|
}
|
|
|
|
if (!res.ok) {
|
|
let body: { error?: string; message?: string } = {};
|
|
try {
|
|
body = (await res.json()) as { error?: string; message?: string };
|
|
} catch {
|
|
// ignore
|
|
}
|
|
throw new ApiError(body.error ?? body.message ?? `Request failed: ${res.status}`, res.status);
|
|
}
|
|
|
|
return res.json() as Promise<T>;
|
|
}
|