|
@@ -0,0 +1,24 @@
|
|
|
|
|
+class ApiError extends Error {
|
|
|
|
|
+ constructor(
|
|
|
|
|
+ readonly status: number,
|
|
|
|
|
+ readonly code: string,
|
|
|
|
|
+ message: string,
|
|
|
|
|
+ ) {
|
|
|
|
|
+ super(message);
|
|
|
|
|
+ this.name = 'ApiError';
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const extractMessage = (err: unknown): string => {
|
|
|
|
|
+ if (err instanceof ApiError) return err.message;
|
|
|
|
|
+ if (err instanceof Error) return err.message;
|
|
|
|
|
+ return 'An unexpected error occurred';
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const handleResponse = async <T>(res: Response): Promise<T> => {
|
|
|
|
|
+ if (res.ok) return res.json() as Promise<T>;
|
|
|
|
|
+ const body = await res.json().catch(() => ({ code: 'UNKNOWN', message: res.statusText }));
|
|
|
|
|
+ throw new ApiError(res.status, body.code ?? 'UNKNOWN', body.message ?? res.statusText);
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+export { ApiError, extractMessage, handleResponse };
|