102 lines
2.6 KiB
TypeScript
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);
|
|
}
|
|
} |