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' } });

Tags

typescript api generics fetch

Related Snippets

javascript

Express Global Error Handler

python

REST API Client with Retry

typescript

Zod Validation Schema

typescript

React Custom Hook Patterns