How to Send JSON Data in a Node.js Fetch POST Request
To send JSON data in a Node.js fetch POST request, serialize your payload with JSON.stringify(), set the Content-Type header to application/json, and include duplex: 'half' in the request options to satisfy the Undici-based implementation.
Node.js ships with a native Fetch API (exposed as globalThis.fetch) built on top of the Undici library, eliminating the need for external HTTP clients. When constructing a nodejs fetch post request that transmits JSON payloads, you must account for specific internal validation rules in the Undici source code that differ from browser implementations.
Why Node.js Fetch Requires the duplex Option
The Node.js fetch implementation lives in the deps/undici/src/lib/web/fetch/ directory of the Node.js source repository. Unlike browser fetch, the Undici-based Request class enforces strict body handling rules for streaming compatibility.
In deps/undici/src/lib/web/fetch/request.js, the constructor validates that any request containing a body (such as POST, PUT, or PATCH) must specify the duplex option. Lines 512-518 contain the validation logic:
if (init.body != null) {
// ...
if (!init.duplex) {
throw new TypeError('RequestInit: duplex option is required when sending a body.');
}
}
Setting duplex: 'half' signals that the request body will be streamed only once, which is the standard behavior for JSON payloads and satisfies the WHATWG Fetch spec requirements as implemented in Node.js.
Complete Example: Sending JSON in a Node.js Fetch POST Request
To correctly transmit JSON data, follow these implementation steps:
- Serialize the payload using
JSON.stringify() - Set the Content-Type header to
application/json - Include
duplex: 'half'in the request initialization - Specify the HTTP method as
POST
Here is a production-ready implementation:
// post-json-example.mjs
const payload = {
username: 'jdoe',
email: '[email protected]',
roles: ['admin', 'user']
};
async function postJson(url, data) {
const response = await fetch(url, {
method: 'POST',
// Required for Node.js fetch when sending a body
duplex: 'half',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify(data)
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
}
// Usage
postJson('https://api.example.com/users', payload)
.then(data => console.log('Success:', data))
.catch(error => console.error('Error:', error));
Handling the Response
After sending the request, the fetch promise resolves to a Response object implemented in deps/undici/src/lib/web/fetch/response.js. For JSON APIs, use the .json() method to parse the response body:
const data = await response.json();
This method returns a promise that resolves to the parsed JavaScript object, handling the underlying stream consumption automatically.
Summary
- Node.js provides a native Fetch API built on Undici, accessible via
globalThis.fetchwithout installing external packages - When sending JSON in a
nodejs fetch post request, you must includeduplex: 'half'to satisfy the body validation indeps/undici/src/lib/web/fetch/request.js - Always serialize your payload with
JSON.stringify()and set theContent-Type: application/jsonheader - The implementation files reside in
deps/undici/src/lib/web/fetch/within the Node.js source repository
Frequently Asked Questions
Why do I get "RequestInit: duplex option is required when sending a body" in Node.js?
This error originates from the Request class constructor in deps/undici/src/lib/web/fetch/request.js. Node.js fetch requires the duplex option for any request containing a body to handle streaming correctly according to the WHATWG Fetch specification. Set duplex: 'half' in your request options to resolve this error.
Do I need to install node-fetch or undici to use fetch in modern Node.js?
No. Since Node.js v18, the Fetch API is available globally as globalThis.fetch without installing external packages. The implementation is built on Undici, which is bundled directly into the Node.js runtime in deps/undici/.
What is the difference between duplex: 'half' and duplex: 'full'?
The duplex option controls how the request body stream is handled. 'half' indicates that the client sends the body once and does not need to read from it again, which is standard for JSON POST requests. 'full' duplex would allow bidirectional streaming, but the Node.js fetch implementation primarily expects 'half' for typical HTTP requests with bodies.
Should I use JSON.stringify() or can I pass the object directly to the body?
You must use JSON.stringify(). The fetch body option accepts various types including strings, but passing a raw JavaScript object will result in the object being converted to [object Object] rather than valid JSON. Always serialize your payload with JSON.stringify() and set the appropriate Content-Type header.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →