2026-02-08 17:39:53 +07:00

140 lines
3.2 KiB
TypeScript

// API Client for CLQMS
// Wrapper around fetch for making API calls to the backend
import { browser } from '$app/environment';
import { PUBLIC_API_BASE_URL, PUBLIC_API_BASE_URL_PROD } from '$env/static/public';
const API_BASE_URL = browser
? (import.meta.env.DEV ? PUBLIC_API_BASE_URL : PUBLIC_API_BASE_URL_PROD)
: PUBLIC_API_BASE_URL;
interface ApiError {
message: string;
status: number;
code?: string;
}
interface ApiResponse<T> {
data: T | null;
error: ApiError | null;
success: boolean;
}
class ApiClient {
private baseUrl: string;
private defaultHeaders: Record<string, string>;
constructor(baseUrl: string = API_BASE_URL) {
this.baseUrl = baseUrl.replace(/\/$/, '');
this.defaultHeaders = {
'Content-Type': 'application/json',
'Accept': 'application/json'
};
}
private async request<T>(
endpoint: string,
options: RequestInit = {}
): Promise<ApiResponse<T>> {
const url = `${this.baseUrl}/${endpoint.replace(/^\//, '')}`;
const config: RequestInit = {
...options,
headers: {
...this.defaultHeaders,
...options.headers
},
credentials: 'include'
};
try {
const response = await fetch(url, config);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
return {
data: null,
error: {
message: errorData.message || `HTTP Error: ${response.status}`,
status: response.status,
code: errorData.code
},
success: false
};
}
const contentType = response.headers.get('content-type');
let data: T;
if (contentType?.includes('application/json')) {
data = await response.json();
} else {
data = await response.text() as unknown as T;
}
return {
data,
error: null,
success: true
};
} catch (error) {
return {
data: null,
error: {
message: error instanceof Error ? error.message : 'Network error',
status: 0
},
success: false
};
}
}
async get<T>(endpoint: string, headers?: Record<string, string>): Promise<ApiResponse<T>> {
return this.request<T>(endpoint, { method: 'GET', headers });
}
async post<T>(
endpoint: string,
body: unknown,
headers?: Record<string, string>
): Promise<ApiResponse<T>> {
return this.request<T>(endpoint, {
method: 'POST',
body: JSON.stringify(body),
headers
});
}
async patch<T>(
endpoint: string,
body: unknown,
headers?: Record<string, string>
): Promise<ApiResponse<T>> {
return this.request<T>(endpoint, {
method: 'PATCH',
body: JSON.stringify(body),
headers
});
}
async put<T>(
endpoint: string,
body: unknown,
headers?: Record<string, string>
): Promise<ApiResponse<T>> {
return this.request<T>(endpoint, {
method: 'PUT',
body: JSON.stringify(body),
headers
});
}
async delete<T>(endpoint: string, headers?: Record<string, string>): Promise<ApiResponse<T>> {
return this.request<T>(endpoint, { method: 'DELETE', headers });
}
}
export const api = new ApiClient();
export type { ApiResponse, ApiError };