Send an HTTP POST request from Node.js

Send JSON with fetch, the built-in HTTP modules, or Axios, while handling status codes, response bodies, and timeouts explicitly.

Best practices
Illustration of an event loop and asynchronous tasks

Use fetch() for a straightforward POST request on a supported Node.js release. The built-in http and https modules offer lower-level control; Axios is useful when a project already relies on its client configuration and interceptors.

Send JSON with fetch

Save this as an ES module, such as post.mjs, and replace the example endpoint with your API:

const response = await fetch('https://example.com/api', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ key1: 'value1', key2: 'value2' }),
  signal: AbortSignal.timeout(5000),
});
if (!response.ok) {
  const detail = await response.text();
  throw new Error(`HTTP ${response.status}: ${detail.slice(0, 200)}`);
}
const data = response.status === 204 ? null : await response.json();
console.log(data);

Global fetch became available without its enabling flag in Node 18 and became stable in Node 21. Use a currently supported release rather than choosing an old release solely because it has fetch. Top-level await requires an ES module; in CommonJS, put it inside an async function.

Fetch rejects network failures but does not reject merely because a server returns 404 or 500. This example expects JSON on successful responses other than 204; adapt parsing to the endpoint's contract. Avoid exposing sensitive error bodies in production logs.

Use the built-in HTTPS client

This CommonJS example handles a textual response:

const https = require('node:https');
const payload = JSON.stringify({ key1: 'value1', key2: 'value2' });
const request = https.request('https://example.com/api', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Content-Length': Buffer.byteLength(payload),
  },
}, (response) => {
  response.setEncoding('utf8');
  response.on('data', (chunk) => process.stdout.write(chunk));
  response.on('error', (error) => console.error(error.message));
  response.on('end', () => {
    console.log(`\nHTTP ${response.statusCode}`);
  });
});
request.on('error', (error) => console.error(error.message));
request.setTimeout(5000, () => request.destroy(new Error('Socket timed out')));
request.end(payload);

Use node:http and an http: URL for plain HTTP. Set both the protocol and destination correctly; changing the module alone is not enough. A known byte length is useful, but Content-Length is not universally mandatory: HTTP/1.1 can frame a request with chunked transfer encoding. end() completes the request body; data can begin transmission before it is called.

The socket timeout above is an inactivity timeout, not a guaranteed total deadline. It only aborts because the callback destroys the request.

Use Axios

After npm install axios, an ES module can use:

import axios from 'axios';

const response = await axios.post('https://example.com/api', {
  key1: 'value1', key2: 'value2',
}, { timeout: 5000 });
console.log(response.data);

Axios serializes a plain object as JSON and normally rejects unsuccessful HTTP statuses. Its timeout must be configured; the default is no timeout. Automatic retries are not a built-in guarantee. Add a deliberate retry policy, especially because repeating a POST can duplicate work.

Choose the level of control

Need Useful starting point
Simple request on modern Node fetch
Existing application client conventions Its existing HTTP client
Interceptors and shared Axios configuration Axios
Direct control over Node request/response streams http or https

FAQ

How do I send URL-encoded fields? Pass new URLSearchParams({ key: 'value' }) as the fetch body. Fetch supplies the corresponding content type unless you override it. Axios also supports URL-encoded bodies.

How do I upload a file? Use FormData with values supported by your client, such as a Blob for built-in fetch. Let the client generate the multipart boundary instead of setting a bare multipart content-type header manually. Consult the client's streaming-upload support for large files.

Does a timeout prove the server did nothing? No. The server may have processed the request before the client stopped waiting. Use idempotency mechanisms where the API provides them.

References

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting