DEV Community

Shin'ya Ueoka
Shin'ya Ueoka

Posted on

Migrating node-fetch/form-data to Node.js native APIs

The release of Node v16.15.0 and v17.5.0 brought in the Fetch API and FormData. Before that, the node-fetch and form-data packages were used to use browser-like APIs in Node.js. To migrate node-fetch/form-data packages to Node.js native APIs, some changes can be made by removing import statements of 3rd-party packages, but there are important considerations to keep in mind when using Node.js features such as file system. This article outlines about migrating from node-fetch/form-data packages to Node.js native APIs.

Overview of migration

 import fs from 'node:fs';
-import FormData from 'form-data';
-import fetch from 'node-fetch';

-const file = fs.createReadStream('secret.txt');
+const file = await fs.openAsBlob('secret.txt', { type: 'text/plain' });
 const form = new FormData();
-form.append('file', file);
+form.append('file', file, 'secret.txt');

 const response = await fetch('https://example.com/upload', {
   method: 'POST',
   body: form,
 });
Enter fullscreen mode Exit fullscreen mode

Details

Incompatibility of types

The form-data package and Node.js FormData are not compatible and cannot be passed directly with the Fetch API. If you pass form-data to the native Fetch API, the request body will be the string [object FormData].

import FormData from 'form-data';

const form = new FormData();
await fetch('https://example.com/upload', {
  method: 'POST',
  body: form,
});

// [object FormData]
Enter fullscreen mode Exit fullscreen mode

The form-data package accepts fs.ReadStream in Node.js. The native API accepts a browser-compatible Blob. To create a Blob from a file, we can sue fs.openAsBlob(),added in Node.js v19.8.0. If you pass fs.ReadStream to the native API's FormData, the value will be [object Object].

import fs from 'node:fs';

const file = fs.createReadStream('secret.txt');
const form = new FormData();
form.append('file', file);

// Content-Disposition: form-data; name="file"
// 
// [object Object]
Enter fullscreen mode Exit fullscreen mode

Incompatibility of internal behavior

The form-data package obtains the file name from fs.ReadStream and uses it as the filename field (internal implementation). It also determines the file type from the file name and sets the Content-Type header (internal implementation).

Blob does not have a file name, it needs to be specified manually. The native API's FormData defaults to application/octet-stream if the Content-Type is not specified. We can specify the Content-Type explicitly from the argument of fs.openAsBlob().

const file = await fs.openAsBlob('secret.txt');
const form = new FormData();
form.append('file', file);

// Content-Disposition: form-data; name="file"; filename="blob"
// Content-Type: application/octet-stream
Enter fullscreen mode Exit fullscreen mode
const file = await fs.openAsBlob('secret.txt', { type: 'text/plain' });
const form = new FormData();
form.append('file', file, 'secret.txt');

// Content-Disposition: form-data; name="file"; filename="secret.txt"
// Content-Type: text/plain
Enter fullscreen mode Exit fullscreen mode

Conclusion

Fetch API and FormData have become Stability: 2 (Stable) from Node.js v21.0.0. fs.openAsBlob() is still Stability: 1 (Experimental) in the latest version of Node.js (v22.5.1 as of at July 27, 2024). We need to take care when running in old runtimes or using experimental APIs which has potential feature changes.

Top comments (0)