HTTP client in JavaScript: making requests correctly — from A to Z
\nIn modern web development, interacting with external APIs and servers is a routine but critically important task. Whether you are writing a microservice on Node.js, parsing data, or creating a bot, you will need a reliable HTTP client. In this article, we will break down how to make HTTP requests in JavaScript (Node.js) correctly, look at popular libraries, error handling, timeouts, and best practices.
\n\n1. Built-in tools: the http module and node-fetch
\nNode.js has built-in http and https modules, but their API is low-level and inconvenient for everyday use. Therefore, wrapper libraries are most often used.
1.1. node-fetch (v3+)
\nStarting from version 18, Node.js has built-in support for fetch (experimental). For older versions or extended functionality, use the node-fetch package.
// Installation: npm install node-fetch\nimport fetch from 'node-fetch';\n\nasync function getUsers() {\n try {\n const response = await fetch('https://jsonplaceholder.typicode.com/users');\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n const data = await response.json();\n console.log(data);\n } catch (error) {\n console.error('Request error:', error.message);\n }\n}\n\ngetUsers();\n\n1.2. Built-in fetch (Node.js 18+)
\nUsed similarly to browser fetch:
\n// Node.js 18+ without additional packages\nasync function postData() {\n const response = await fetch('https://api.example.com/data', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ key: 'value' })\n });\n return response.json();\n}\n\n2. Axios — the gold standard of HTTP clients
\nAxios is the most popular library for HTTP requests in JavaScript. It provides a convenient interface, promise support, interceptors, and automatic JSON transformation.
\n\n2.1. Installation and basic GET request
\n// npm install axios\nconst axios = require('axios');\n\nasync function fetchUsers() {\n try {\n const response = await axios.get('https://jsonplaceholder.typicode.com/users');\n console.log(response.data); // data is already parsed\n } catch (error) {\n console.error('Error:', error.message);\n }\n}\n\n2.2. POST request with data
\nasync function createUser() {\n const newUser = { name: 'Ivan', email: 'ivan@example.com' };\n const response = await axios.post('https://jsonplaceholder.typicode.com/users', newUser);\n console.log('User created:', response.data);\n}\n\n2.3. Configuring timeout and headers
\nTimeout is a critically important option to prevent application hanging:
\nasync function fetchWithTimeout() {\n try {\n const response = await axios.get('https://slow-api.example.com/data', {\n timeout: 5000, // 5 seconds\n headers: {\n 'Authorization': 'Bearer token123',\n 'Accept': 'application/json'\n }\n });\n return response.data;\n } catch (error) {\n if (error.code === 'ECONNABORTED') {\n console.error('Request timeout');\n }\n }\n}\n\n3. Advanced techniques and best practices
\n\n3.1. Error handling and retries
\nNetworks are unstable, so it is important to implement a retry mechanism:
\nasync function fetchWithRetry(url, retries = 3) {\n for (let i = 0; i < retries; i++) {\n try {\n const response = await axios.get(url);\n return response.data;\n } catch (error) {\n if (i === retries - 1) throw error; // last attempt\n console.log(`Attempt ${i + 1} failed, retrying in ${(i + 1) * 1000}ms...`);\n await new Promise(