Sending form data using fetch in Node.js

Mayank Choubey
Tech Tonic
Published in
3 min readMay 12, 2023

--

Ever since Node.js got support of the fetch web API, it has been becoming the standard practice for sending HTTP requests (aka HTTP client). For simple data types like string, JSON, or URL encoded, fetch is pretty easy to use. However, the fetch API gets a bit tricky for form uploads (aka multipart/form-data). In this brief article, I’m going to cover how to do form uploads in Node.js using fetch. The other reason to write is that, when I needed information on this topic, I couldn’t find any contemporary articles. All I could find were old articles talking about node-fetch, request, axios, etc.

Sending form data

Along with the web standard fetch API, Node also supports web standard interfaces like FormData & Blob, which are useful in sending form data using fetch. An object of type FormData can be directly used as the fetch body.

The following is the code for setting simple form fields in a fetch request:

(I’m using an echo service for testing, that simply returns the request body to the caller)

For this example, I’m setting two form fields: field1 with val1 and field2 with val2.

const body = new FormData();
body.set("field1", "val1");
body.set("field2", "val2");

const resp = await fetch("https://echo-server.deno.dev", {
method: "POST",
body,
});

console.log(
"STATUS:",
resp.status,
"\nCONTENT TYPE:",
resp.headers.get("content-type"),
);
console.log("RAW BODY:", await resp.text());

Here is the output of a quick run:

$ node fetch_fd.mjs 
STATUS: 200
CONTENT TYPE: multipart/form-data; boundary=----formdata-undici-023486147632
RAW BODY: ------formdata-undici-023486147632
Content-Disposition: form-data; name="field1"

val1
------formdata-undici-023486147632
Content-Disposition: form-data; name="field2"

val2
------formdata-undici-023486147632--

Looks good. The echo service is returning the request which I’m printing using text API.

Now, let’s move on to adding a file to the form data request. For adding files, the form data set API needs a web standard Blob object. A Blob object can be prepared from a file buffer using Node’s readFile API.

const rawData = await readFile('./someFile');
const someBlob = new Blob([rawData]);

Here is the updated code that sends two fields and a file:

import { readFile } from "node:fs/promises";

const fileName = "./sample.txt";
const body = new FormData();
const blob = new Blob([await readFile(fileName)]);
body.set("field1", "val1");
body.set("field2", "val2");
body.set("file1", blob, fileName);

const resp = await fetch("https://echo-server.deno.dev", {
method: "POST",
body,
});

console.log(
"STATUS:",
resp.status,
"\nCONTENT TYPE:",
resp.headers.get("content-type"),
);
console.log("RAW BODY:", await resp.text());

Here is the output of a quick run:

$ node fetch_fd.mjs 
STATUS: 200
CONTENT TYPE: multipart/form-data; boundary=----formdata-undici-069031863955
RAW BODY: ------formdata-undici-069031863955
Content-Disposition: form-data; name="field1"

val1
------formdata-undici-069031863955
Content-Disposition: form-data; name="field2"

val2
------formdata-undici-069031863955
Content-Disposition: form-data; name="file1"; filename="./sample.txt"
Content-Type: application/octet-stream

I'm
learning
how
to
send
form
data
in
Node.js

------formdata-undici-069031863955--

Works perfectly!

If you want to learn how to handle URL encoded data using fetch, the article is here:

If you also want to know about how to download a file using fetch (streaming download), the article is here:

--

--