HTTP client in TypeScript: making requests correctly from A to Z

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 from A to Z

\n\n

Modern web development is unthinkable without interacting with remote servers. Whether it's fetching data from a REST API, submitting forms, or uploading files — HTTP requests are the foundation of client-server architecture. TypeScript, with its strict typing and powerful tools, makes working with HTTP requests not only safe but also convenient for maintenance. In this article, we'll break down how to create a well-designed HTTP client in TypeScript, avoid common mistakes, and write code that behaves predictably.

\n\n

1. Basics: fetch API and its typing

\n\n

The browser's built-in fetch is a modern and flexible tool for making HTTP requests. However, using it in TypeScript requires proper typing of the response. By default, fetch returns Promise<Response>, and we need to explicitly specify what data we expect to receive.

\n\n
interface User {\n  id: number;\n  name: string;\n  email: string;\n}\n\nasync function getUser(id: number): Promise<User> {\n  const response = await fetch(`https://api.example.com/users/${id}`);\n  \n  if (!response.ok) {\n    throw new Error(`HTTP error! status: ${response.status}`);\n  }\n  \n  const data: User = await response.json();\n  return data;\n}\n
\n\n

Note the check for response.ok. This is critically important: even if the server returns an error (404, 500, etc.), fetch will not throw an exception automatically. We must handle the status manually.

\n\n

For POST requests with a body, you need to specify the Content-Type header:

\n\n
async function createUser(userData: Omit<User, 'id'>): Promise<User> {\n  const response = await fetch('https://api.example.com/users', {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n    },\n    body: JSON.stringify(userData),\n  });\n\n  if (!response.ok) {\n    throw new Error(`Failed to create user: ${response.statusText}`);\n  }\n\n  return response.json();\n}\n
\n\n

2. Creating a universal HTTP client

\n\n

Instead of writing fetch from scratch every time, it's better to create a wrapper — a universal HTTP client. This allows you to centrally manage the base URL, headers, and error handling. Let's look at an example based on classes:

\n\n
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n\ninterface RequestConfig<TBody = unknown> {\n  method: HttpMethod;\n  url: string;\n  body?: TBody;\n  headers?: Record<string, string>;\n  params?: Record<string, string>;\n}\n\nclass HttpClient {\n  private baseUrl: string;\n  private defaultHeaders: Record<string, string>;\n\n  constructor(baseUrl: string, defaultHeaders: Record<string, string> = {}) {\n    this.baseUrl = baseUrl;\n    this.defaultHeaders = {\n      'Content-Type': 'application/json',\n      ...defaultHeaders,\n    };\n  }\n\n  private async request<TResponse, TBody = unknown>(\n    config: RequestConfig<TBody>\n  ): Promise<TResponse> {\n    const url = new URL(`${this.baseUrl}${config.url}`);\n    \n    if (config.params) {\n      Object.entries(config.params).forEach(([key, value]) => {\n        url.searchParams.append(key, value);\n      });\n    }\n\n    const response = await fetch(url.toString(), {\n      method: config.method,\n      headers: { ...this.defaultHeaders, ...config.headers },\n      body: config.body ? JSON.stringify(config.body) : undefined,\n    });\n\n    if (!response.ok) {\n      const errorBody = await response.text();\n      throw new Error(`Request failed: ${response.status} ${response.statusText}. Body: ${errorBody}`);\n    }\n\n    // For DELETE or other methods that may not return a body\n    if (response.status === 204) {\n      return undefined as TResponse;\n    }\n\n    return response.jso                

Blogs

Book Recommendations