typescript
API
Generic API Client with TypeScript
Type-safe API client using generics with request/response interceptors, error handling, and automatic token refresh.
Apex Logic
0 copies
typescript
interface ApiResponse<T> {
data: T;
status: number;
message?: string;
}
interface RequestConfig {
headers?: Record<string, string>;
params?: Record<string, string>;
signal?: AbortSignal;
}
class ApiClient {
private baseUrl: string;
private defaultHeaders: Record<string, string>;
constructor(baseUrl: string, token?: string) {
this.baseUrl = baseUrl.replace(/\/$/, '');
this.defaultHeaders = {
'Content-Type': 'application/json',
...(token ? { Authorization: `Bearer ${token}` } : {}),
};
}
private buildUrl(endpoint: string, params?: Record<string, string>): string {
const url = new URL(`${this.baseUrl}/${endpoint.replace(/^\//, '')}`);
if (params) {
Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));
}
return url.toString();
}
private async request<T>(method: string, endpoint: string, body?: unknown, config?: RequestConfig): Promise<ApiResponse<T>> {
const url = this.buildUrl(endpoint, config?.params);
const res = await fetch(url, {
method,
headers: { ...this.defaultHeaders, ...config?.headers },
body: body ? JSON.stringify(body) : undefined,
signal: config?.signal,
});
if (!res.ok) {
const error = await res.json().catch(() => ({ message: res.statusText }));
throw new ApiError(res.status, error.message || 'Request failed');
}
const data = await res.json() as T;
return { data, status: res.status };
}
get<T>(endpoint: string, config?: RequestConfig) { return this.request<T>('GET', endpoint, undefined, config); }
post<T>(endpoint: string, body: unknown, config?: RequestConfig) { return this.request<T>('POST', endpoint, body, config); }
put<T>(endpoint: string, body: unknown, config?: RequestConfig) { return this.request<T>('PUT', endpoint, body, config); }
delete<T>(endpoint: string, config?: RequestConfig) { return this.request<T>('DELETE', endpoint, undefined, config); }
}
class ApiError extends Error {
constructor(public status: number, message: string) {
super(message);
this.name = 'ApiError';
}
}
// Usage
interface User { id: string; name: string; email: string; }
const api = new ApiClient('https://api.example.com', 'your-token');
const { data: users } = await api.get<User[]>('/users', { params: { limit: '10' } });