refactor(shared): update utilities, API layer, and types

This commit is contained in:
Ilia Mashkov
2026-03-02 22:19:13 +03:00
parent ac73fd5044
commit 13818d5844
17 changed files with 554 additions and 96 deletions
+83
View File
@@ -1,9 +1,50 @@
/**
* HTTP API client with error handling
*
* Provides a typed wrapper around fetch for JSON APIs.
* Automatically handles JSON serialization and error responses.
*
* @example
* ```ts
* import { api } from '$shared/api';
*
* // GET request
* const users = await api.get<User[]>('/api/users');
*
* // POST request
* const newUser = await api.post<User>('/api/users', { name: 'Alice' });
*
* // Error handling
* try {
* const data = await api.get('/api/data');
* } catch (error) {
* if (error instanceof ApiError) {
* console.error(error.status, error.message);
* }
* }
* ```
*/
import type { ApiResponse } from '$shared/types/common';
/**
* Custom error class for API failures
*
* Includes HTTP status code and the original Response object
* for debugging and error handling.
*/
export class ApiError extends Error {
/**
* Creates a new API error
* @param status - HTTP status code
* @param message - Error message
* @param response - Original fetch Response object
*/
constructor(
/** HTTP status code */
public status: number,
message: string,
/** Original Response object for inspection */
public response?: Response,
) {
super(message);
@@ -11,6 +52,16 @@ export class ApiError extends Error {
}
}
/**
* Internal request handler
*
* Performs fetch with JSON headers and throws ApiError on failure.
*
* @param url - Request URL
* @param options - Fetch options (method, headers, body, etc.)
* @returns Response data and status code
* @throws ApiError when response is not OK
*/
async function request<T>(
url: string,
options?: RequestInit,
@@ -39,9 +90,28 @@ async function request<T>(
};
}
/**
* API client methods
*
* Provides typed methods for common HTTP verbs.
* All methods return ApiResponse with data and status.
*/
export const api = {
/**
* Performs a GET request
* @param url - Request URL
* @param options - Additional fetch options
* @returns Response data
*/
get: <T>(url: string, options?: RequestInit) => request<T>(url, { ...options, method: 'GET' }),
/**
* Performs a POST request with JSON body
* @param url - Request URL
* @param body - Request body (will be JSON stringified)
* @param options - Additional fetch options
* @returns Response data
*/
post: <T>(url: string, body?: unknown, options?: RequestInit) =>
request<T>(url, {
...options,
@@ -49,6 +119,13 @@ export const api = {
body: JSON.stringify(body),
}),
/**
* Performs a PUT request with JSON body
* @param url - Request URL
* @param body - Request body (will be JSON stringified)
* @param options - Additional fetch options
* @returns Response data
*/
put: <T>(url: string, body?: unknown, options?: RequestInit) =>
request<T>(url, {
...options,
@@ -56,5 +133,11 @@ export const api = {
body: JSON.stringify(body),
}),
/**
* Performs a DELETE request
* @param url - Request URL
* @param options - Additional fetch options
* @returns Response data
*/
delete: <T>(url: string, options?: RequestInit) => request<T>(url, { ...options, method: 'DELETE' }),
};