# Node HTTP Request: Built-in Module vs External Libraries – Architecture and Performance Comparison

> Compare Node HTTP request: built-in http module vs. external libraries like Axios. Learn about architecture, performance, promises, and retries to choose the best option for your project.

- Repository: [Node.js/node](https://github.com/nodejs/node)
- Tags: architecture
- Published: 2026-02-16

---

**The built-in `http` module provides zero-dependency, low-level socket control via streams and callbacks, while external libraries like Axios and Got wrap these primitives to offer Promise-based APIs, automatic JSON parsing, and built-in retry logic.**

When implementing a node http request in Node.js, developers face a choice between the built-in `http` module and popular external libraries. The Node.js repository (`nodejs/node`) implements the core HTTP client in [`lib/http.js`](https://github.com/nodejs/node/blob/main/lib/http.js) and [`lib/_http_client.js`](https://github.com/nodejs/node/blob/main/lib/_http_client.js), providing thin wrappers over TCP sockets. External packages build upon these foundations to abstract away stream handling and connection management, trading minimal bundle size for developer ergonomics.

## Abstraction Level and API Design

### Built-in Module: Low-Level Stream Control

The built-in module exposes a callback-based API where you manually assemble request headers and consume response streams. In [`lib/_http_client.js`](https://github.com/nodejs/node/blob/main/lib/_http_client.js), the `ClientRequest` class manages the socket lifecycle, emitting `'data'` events for response chunks that you must manually accumulate or pipe.

Key characteristics include:

- Manual header serialization and body encoding
- Event-driven response handling via `IncomingMessage` streams defined in [`lib/_http_incoming.js`](https://github.com/nodejs/node/blob/main/lib/_http_incoming.js)
- Explicit connection management through the `Agent` class in [`lib/_http_agent.js`](https://github.com/nodejs/node/blob/main/lib/_http_agent.js)

### External Libraries: Declarative Promise-Based Interfaces

External libraries abstract header construction, query-string encoding, and body parsing. They return Promises that resolve to parsed objects, eliminating manual stream handling for common use cases while still allowing streaming via configuration options.

## Feature Set and Developer Experience

The built-in `http` module focuses on core HTTP/1.1 features with basic TLS support through the `https` variant. It provides manual keep-alive via `Agent` instances but lacks built-in retry logic, automatic JSON parsing, or request cancellation mechanisms.

External libraries typically provide:

- **Automatic retries** with configurable backoff strategies and circuit breakers
- **Request/response interceptors** for middleware patterns and logging
- **Timeout shortcuts** and request cancellation tokens
- **Automatic redirect following** and cookie jar management
- **Multipart/form-data** helpers for file uploads

## Performance and Dependency Footprint

The built-in module carries **zero external dependencies**, making it ideal for minimal bundles, AWS Lambda layers, or environments where binary size matters. It offers very low overhead because it interfaces directly with the socket layer via the C++ HTTP parser in `src/node_http_parser.cc`.

External libraries add one or more NPM packages (for example, Axios adds approximately 15 KB gzipped). While this increases bundle size, the performance difference is usually negligible for typical request/response workloads compared to network latency, and the productivity gains often justify the overhead.

## When to Use the Built-in `http` Module

Choose the built-in module when you require:

- **Maximum control** over sockets, TLS negotiation, or keep-alive behavior managed by [`lib/_http_agent.js`](https://github.com/nodejs/node/blob/main/lib/_http_agent.js)
- **Zero-dependency** constraints for serverless functions or minimal Docker images
- **High-throughput proxies** or custom load balancers where every millisecond of overhead matters
- **Streaming-intensive** workloads requiring precise back-pressure handling via `ClientRequest` and `IncomingMessage` interfaces

## When to Prefer External Libraries

External libraries are better suited when you need:

- **Concise, Promise-based** code with `async/await` support and automatic JSON parsing
- **Built-in resilience** features like retries, circuit breakers, and timeout handling without custom implementation
- **Developer ergonomics** such as request cancellation, progress events, and automatic redirect following
- **Rapid application development** where writing boilerplate for stream accumulation and error handling would slow delivery

## Implementation Examples

### Basic Node HTTP Request with Built-in Module

The following example demonstrates the low-level approach using `http.request()` from [`lib/http.js`](https://github.com/nodejs/node/blob/main/lib/http.js). You must manually handle the `'data'` and `'end'` events to accumulate the response body.

```javascript
const http = require('node:http');

const options = {
  hostname: 'api.example.com',
  path: '/data',
  method: 'GET',
  headers: { Accept: 'application/json' },
};

const req = http.request(options, (res) => {
  let rawData = '';
  res.setEncoding('utf8');
  res.on('data', (chunk) => (rawData += chunk));
  res.on('end', () => {
    try {
      const parsed = JSON.parse(rawData);
      console.log('Response:', parsed);
    } catch (e) {
      console.error('Parsing error:', e);
    }
  });
});

req.on('error', (e) => console.error(`Request error: ${e.message}`));
req.end();

```

### Promise-Based Request with Axios

Axios wraps the built-in modules to provide a declarative API with automatic JSON parsing and unified error handling.

```javascript
const axios = require('axios');

(async () => {
  try {
    const response = await axios.get('https://api.example.com/data', {
      headers: { Accept: 'application/json' },
      timeout: 5000,
    });
    console.log('Response data:', response.data);
  } catch (err) {
    if (err.response) {
      console.error('Server responded with status', err.response.status);
    } else {
      console.error('Request failed:', err.message);
    }
  }
})();

```

### Streaming Download with Got

Got demonstrates how external libraries can still expose streaming capabilities while providing higher-level conveniences like progress events.

```javascript
const got = require('got');
const fs = require('node:fs');

(async () => {
  const downloadStream = got.stream('https://example.com/large-file.zip');
  const fileWriter = fs.createWriteStream('large-file.zip');

  downloadStream.pipe(fileWriter);

  downloadStream.on('downloadProgress', ({ transferred, total, percent }) => {
    console.log(`Progress: ${(percent * 100).toFixed(2)}%`);
  });

  fileWriter.on('finish', () => console.log('Download completed'));
})();

```

## Key Source Files in the Node.js Repository

Understanding the architecture requires examining the core implementation files:

- **[`lib/http.js`](https://github.com/nodejs/node/blob/main/lib/http.js)** – Public entry point exposing `http.request()` and server creation APIs.
- **[`lib/_http_client.js`](https://github.com/nodejs/node/blob/main/lib/_http_client.js)** – Implements `ClientRequest` class managing socket lifecycle and header serialization.
- **[`lib/_http_agent.js`](https://github.com/nodejs/node/blob/main/lib/_http_agent.js)** – Manages connection pooling and keep-alive logic via the `Agent` class.
- **[`lib/_http_incoming.js`](https://github.com/nodejs/node/blob/main/lib/_http_incoming.js)** – Defines `IncomingMessage` used for both server requests and client responses.
- **`src/node_http_parser.cc`** – Native C++ HTTP parser handling protocol-level parsing for maximum performance.
- **[`doc/api/http.md`](https://github.com/nodejs/node/blob/main/doc/api/http.md)** – Official documentation describing the API surface and event semantics.

## Summary

- The **built-in `http` module** offers zero-dependency, low-level control over TCP sockets and HTTP streams via `ClientRequest` and `IncomingMessage`, implemented in [`lib/_http_client.js`](https://github.com/nodejs/node/blob/main/lib/_http_client.js) and [`lib/_http_incoming.js`](https://github.com/nodejs/node/blob/main/lib/_http_incoming.js).
- **External libraries** wrap these primitives to provide Promise-based APIs, automatic JSON parsing, retries, and interceptors, trading minimal bundle size for developer productivity.
- Choose the **built-in module** for high-throughput proxies, serverless environments with strict size constraints, or when implementing custom protocols requiring fine-grained socket control.
- Choose **external libraries** for rapid application development, automatic resilience patterns, and modern async/await workflows without boilerplate.

## Frequently Asked Questions

### Is the built-in http module faster than Axios?

For raw throughput, the built-in module has lower overhead because it interfaces directly with the socket layer in [`lib/_http_client.js`](https://github.com/nodejs/node/blob/main/lib/_http_client.js) and the C++ parser in `src/node_http_parser.cc`. However, for typical CRUD operations, the performance difference is negligible compared to network latency, and Axios's convenience features often justify any minor overhead.

### Can I use async/await with the built-in http module?

The built-in module uses EventEmitter and callback patterns rather than native Promises. To use async/await, you must manually wrap `http.request()` in a Promise constructor or use `util.promisify`. External libraries like Axios and Got provide native Promise support without additional wrapping.

### Does the built-in module support HTTP/2?

The built-in `http` module in [`lib/http.js`](https://github.com/nodejs/node/blob/main/lib/http.js) implements HTTP/1.1 only. For HTTP/2 support, Node.js provides a separate `http2` module ([`lib/http2.js`](https://github.com/nodejs/node/blob/main/lib/http2.js)) with a different API surface. Some external libraries can abstract both protocols, automatically selecting HTTP/2 when available.

### When should I choose Got over Axios for node http requests?

Choose **Got** when you need advanced streaming capabilities with progress events, as it provides a dedicated `got.stream()` interface and built-in download progress tracking. Choose **Axios** when you require request/response interceptors, broader browser compatibility, or when working with teams already familiar with its config-based API. Both ultimately rely on the same [`lib/_http_client.js`](https://github.com/nodejs/node/blob/main/lib/_http_client.js) primitives.