HTTP Client in TypeScript: Making Requests the Right Way

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

HTTP client in TypeScript: making requests correctly



In modern web development, working with HTTP requests is one of the key tasks. TypeScript, as a strictly typed superset of JavaScript, provides developers with powerful tools for creating reliable and predictable HTTP clients. In this article, we will break down how to properly organize an HTTP client in TypeScript so that your code is safe, readable, and easily maintainable.



Why is TypeScript ideal for HTTP clients?



TypeScript adds static typing, which is especially important when working with external APIs. You know exactly what data will come from the server and can prevent many errors at the compilation stage. Let's look at a basic example:



// Bad: JavaScript without typesconst data = await fetch('/api/users');const users = await data.json();// We don't know the structure of users

// Good: TypeScript with typesinterface User { id: number; name: string; email: string;}

const response = await fetch('/api/users');const users: User[] = await response.json();// Now users has a clear structure


Creating a basic HTTP client in TypeScript



Let's start by creating a universal HTTP client that will handle all types of requests. We'll use the native fetch API, which is available in modern browsers and Node.js 18+.



Basic implementation with typing



interface HttpClientConfig {  baseURL: string;  headers?: Record<string, string>;  timeout?: number;}

interface ApiResponse<T> { data: T; status: number; ok: boolean;}

class HttpClient { private baseURL: string; private headers: Record<string, string>; private timeout: number;

constructor(config: HttpClientConfig) { this.baseURL = config.baseURL; this.headers = { 'Content-Type': 'application/json', ...config.headers, }; this.timeout = config.timeout || 10000; }

private async request<T>( endpoint: string, options: RequestInit = {} ): Promise<ApiResponse<T>> { const url = `${this.baseURL}${endpoint}`; const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), this.timeout);

try { const response = await fetch(url, { ...options, headers: { ...this.headers, ...options.headers }, signal: controller.signal, });

clearTimeout(timeoutId);

const data: T = await response.json();

return { data, status: response.status, ok: response.ok, }; } catch (error) { clearTimeout(timeoutId); throw this.handleError(error); } }

private handleError(error: unknown): Error { if (error instanceof DOMException && error.name === 'AbortError') { return new Error('Request timeout'); } if (error instanceof TypeError) { return new Error('Network error'); } return error as Error; }

async get<T>(endpoint: string): Promise<ApiResponse<T>> { return this.request<T>(endpoint, { method: 'GET' }); }

async post<T>(endpoint: string, body: unknown): Promise<ApiResponse<T>> { return this.request<T>(endpoint, { method: 'POST', body: JSON.stringify(body), }); }

async put<T>(endpoint: string, body: unknown): Promise<ApiResponse<T>> { return this.request<T>(endpoint, { method: 'PUT', body: JSON.stringify(body), }); }

async delete<T>(endpoint: string): Promise<ApiResponse<T>> { return this.request<T>(endpoint, { method: 'DELETE' }); }}


Using the client



// Create a client instanceconst api = new HttpClient({  baseURL: 'https://api.                

Blogs

Book Recommendations