calendi/frontend/src/lib/api/client.ts
2025-04-26 02:27:06 +02:00

102 lines
2.6 KiB
TypeScript

export type ApiResponse<T> = {
data: T;
success: boolean;
error?: string;
};
export type RequestOptions = {
params?: Record<string, string | number | boolean>;
headers?: Record<string, string>;
};
const API_BASE_URL = process.env.REACT_APP_API_URL || 'http://calendi.test';
export class ApiError extends Error {
status: number;
constructor(message: string, status: number) {
super(message);
this.status = status;
this.name = 'ApiError';
}
}
export async function get<T>(endpoint: string, options?: RequestOptions): Promise<T> {
const url = buildUrl(endpoint, options?.params);
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
...options?.headers
}
});
return handleResponse<T>(response);
}
export async function post<T>(endpoint: string, data: unknown, options?: RequestOptions): Promise<T> {
const url = buildUrl(endpoint, options?.params);
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...options?.headers
},
body: JSON.stringify(data)
});
return handleResponse<T>(response);
}
export async function put<T>(endpoint: string, data: unknown, options?: RequestOptions): Promise<T> {
const url = buildUrl(endpoint, options?.params);
const response = await fetch(url, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
...options?.headers
},
body: JSON.stringify(data)
});
return handleResponse<T>(response);
}
export async function del<T>(endpoint: string, options?: RequestOptions): Promise<T> {
const url = buildUrl(endpoint, options?.params);
const response = await fetch(url, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
...options?.headers
}
});
return handleResponse<T>(response);
}
function buildUrl(endpoint: string, params?: Record<string, string | number | boolean>): string {
const url = new URL(`${API_BASE_URL}${endpoint}`);
url.port = window.location.port;
if (params) {
Object.entries(params).forEach(([key, value]) => {
url.searchParams.append(key, String(value));
});
}
return url.toString();
}
async function handleResponse<T>(response: Response): Promise<T> {
if (!response.ok) {
const errorText = await response.text();
throw new ApiError(errorText || `Request failed with status ${response.status}`, response.status);
}
try {
return await response.json() as T;
} catch (error) {
throw new ApiError('Failed to parse response', 500);
}
}