96 lines
2.4 KiB
TypeScript
96 lines
2.4 KiB
TypeScript
/**
|
|
* API Client Configuration
|
|
*/
|
|
export interface ClientConfig {
|
|
baseUrl: string;
|
|
apiKey?: string;
|
|
bearerToken?: string;
|
|
headers?: Record<string, string>;
|
|
onError?: (error: Error) => void;
|
|
}
|
|
|
|
/**
|
|
* Create a typed API client
|
|
*
|
|
* @example
|
|
* const client = createClient({
|
|
* baseUrl: 'https://api.example.com',
|
|
* apiKey: 'your-api-key',
|
|
* });
|
|
*
|
|
* const users = await client.get('/users');
|
|
* const newUser = await client.post('/users', { name: 'John' });
|
|
*/
|
|
export function createClient(config: ClientConfig) {
|
|
const { baseUrl, apiKey, bearerToken, headers = {}, onError } = config;
|
|
|
|
async function request<T>(
|
|
method: string,
|
|
path: string,
|
|
options: {
|
|
body?: unknown;
|
|
params?: Record<string, string | number | boolean | undefined>;
|
|
headers?: Record<string, string>;
|
|
} = {}
|
|
): Promise<T> {
|
|
const url = new URL(path, baseUrl);
|
|
|
|
// Add query params
|
|
if (options.params) {
|
|
for (const [key, value] of Object.entries(options.params)) {
|
|
if (value !== undefined) {
|
|
url.searchParams.set(key, String(value));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Build headers
|
|
const requestHeaders: Record<string, string> = {
|
|
'Content-Type': 'application/json',
|
|
...headers,
|
|
...options.headers,
|
|
};
|
|
|
|
if (apiKey) {
|
|
requestHeaders['X-API-Key'] = apiKey;
|
|
}
|
|
if (bearerToken) {
|
|
requestHeaders['Authorization'] = `Bearer ${bearerToken}`;
|
|
}
|
|
|
|
const response = await fetch(url.toString(), {
|
|
method,
|
|
headers: requestHeaders,
|
|
body: options.body ? JSON.stringify(options.body) : undefined,
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const error = new Error(`API error: ${response.status}`);
|
|
if (onError) {
|
|
onError(error);
|
|
}
|
|
throw error;
|
|
}
|
|
|
|
// Handle no-content responses
|
|
if (response.status === 204) {
|
|
return undefined as T;
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
return {
|
|
get: <T>(path: string, params?: Record<string, string | number | boolean | undefined>) =>
|
|
request<T>('GET', path, { params }),
|
|
post: <T>(path: string, body?: unknown) =>
|
|
request<T>('POST', path, { body }),
|
|
put: <T>(path: string, body?: unknown) =>
|
|
request<T>('PUT', path, { body }),
|
|
patch: <T>(path: string, body?: unknown) =>
|
|
request<T>('PATCH', path, { body }),
|
|
delete: <T>(path: string) =>
|
|
request<T>('DELETE', path),
|
|
};
|
|
}
|