/** * Base API Client for HTTP operations * * Provides generic HTTP methods with common request/response handling, * error handling, and authentication. */ import { Logger } from '../../interfaces/Logger'; import { ErrorReporter } from '../../interfaces/ErrorReporter'; export class BaseApiClient { protected baseUrl: string; private errorReporter: ErrorReporter; private logger: Logger; constructor(baseUrl: string, errorReporter: ErrorReporter, logger: Logger) { this.baseUrl = baseUrl; this.errorReporter = errorReporter; this.logger = logger; } protected async request(method: string, path: string, data?: object | FormData): Promise { this.logger.info(`${method} ${path}`); const isFormData = typeof FormData !== 'undefined' && data instanceof FormData; const headers: HeadersInit = isFormData ? {} : { 'Content-Type': 'application/json', }; const config: RequestInit = { method, headers, credentials: 'include', // Include cookies for auth }; if (data) { config.body = isFormData ? data : JSON.stringify(data); } const response = await fetch(`${this.baseUrl}${path}`, config); if (!response.ok) { let errorData: { message?: string } = { message: response.statusText }; try { errorData = await response.json(); } catch { // Keep default error message } const error = new Error( errorData.message || `API request failed with status ${response.status}`, ) as Error & { status?: number }; error.status = response.status; this.errorReporter.report(error); throw error; } const text = await response.text(); if (!text) { return null as T; } return JSON.parse(text) as T; } protected get(path: string): Promise { return this.request('GET', path); } protected post(path: string, data: object): Promise { return this.request('POST', path, data); } protected put(path: string, data: object): Promise { return this.request('PUT', path, data); } protected delete(path: string): Promise { return this.request('DELETE', path); } protected patch(path: string, data: object): Promise { return this.request('PATCH', path, data); } }