TypeScript Generic API Client

Type-safe fetch wrapper with generics, error handling, and abort support.

TypeScript Generic API Client
type ApiResponse<T> = { data: T; error: null } | { data: null; error: string };

async function apiClient<T>(
  url: string,
  options?: RequestInit & { signal?: AbortSignal }
): Promise<ApiResponse<T>> {
  try {
    const res = await fetch(url, {
      headers: { 'Content-Type': 'application/json' },
      ...options,
    });

    if (!res.ok) {
      const msg = await res.text();
      return { data: null, error: msg || res.statusText };
    }

    const data: T = await res.json();
    return { data, error: null };
  } catch (err) {
    return { data: null, error: (err as Error).message };
  }
}

// Usage
interface User { id: number; name: string }

const { data, error } = await apiClient<User[]>('/api/users');
if (error) console.error(error);
else console.log(data);