Frontend start to use backend CRUD api
This commit is contained in:
58
web/services/apiClient.ts
Normal file
58
web/services/apiClient.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
const DEFAULT_API_BASE_URL = 'http://localhost:8000/api';
|
||||
|
||||
const trimTrailingSlash = (value: string): string => value.replace(/\/+$/, '');
|
||||
|
||||
const getApiBaseUrl = (): string => {
|
||||
const configured = import.meta.env.VITE_API_BASE_URL || DEFAULT_API_BASE_URL;
|
||||
return trimTrailingSlash(configured);
|
||||
};
|
||||
|
||||
type RequestOptions = {
|
||||
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
||||
body?: unknown;
|
||||
signal?: AbortSignal;
|
||||
headers?: Record<string, string>;
|
||||
};
|
||||
|
||||
export class ApiError extends Error {
|
||||
status: number;
|
||||
|
||||
constructor(message: string, status: number) {
|
||||
super(message);
|
||||
this.name = 'ApiError';
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
|
||||
export const apiRequest = async <T>(path: string, options: RequestOptions = {}): Promise<T> => {
|
||||
const url = `${getApiBaseUrl()}${path.startsWith('/') ? path : `/${path}`}`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: options.method || 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers,
|
||||
},
|
||||
body: options.body === undefined ? undefined : JSON.stringify(options.body),
|
||||
signal: options.signal,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
let message = `Request failed: ${response.status}`;
|
||||
try {
|
||||
const errorData = await response.json();
|
||||
if (errorData?.detail) {
|
||||
message = typeof errorData.detail === 'string' ? errorData.detail : message;
|
||||
}
|
||||
} catch {
|
||||
// Ignore parsing errors.
|
||||
}
|
||||
throw new ApiError(message, response.status);
|
||||
}
|
||||
|
||||
if (response.status === 204) {
|
||||
return undefined as T;
|
||||
}
|
||||
|
||||
return response.json() as Promise<T>;
|
||||
};
|
||||
Reference in New Issue
Block a user