← Back to archive

Repo of the Day

node-fetch/node-fetch

Published: Mar 2, 2023

Open repository ↗

An oldie but a goodie I think. Using it for a script I just made that gets a static json file from a url and copies it to a local folder for mocking on VA. Probably not required since Node natively supports fetch, but good nonetheless to look at.

Summary

node-fetch is a lightweight JavaScript module that implements the WHATWG Fetch API for the Node.js runtime, so server-side code can use the same fetch() interface browsers expose. v3 is ESM-only and requires Node.js 12.20.0 or newer; v2 is still maintained for projects that need CommonJS require().

What it is useful for

node-fetch is useful when Node.js code needs to make HTTP or HTTPS calls using the browser-style fetch(url, options) rather than the built-in http/https modules. It returns a Response whose body can be read with response.text(), response.json(), response.arrayBuffer(), response.blob(), or response.formData(), and response.body is a native Node Readable stream that can be piped elsewhere.

Jobs it fits:

  • Calling REST APIs and parsing JSON with await response.json()
  • POSTing form data via URLSearchParams (the Content-Type is set automatically only when an instance is passed)
  • POSTing JSON by stringifying the object and setting the header manually
  • POSTing files via fileFromSync(), or building multipart payloads with the spec-compliant FormData, File, and Blob it ships
  • Streaming large responses via stream.pipeline(response.body, writable) or for await (const chunk of response.body)
  • Canceling requests with AbortController/AbortSignal, catching the exported AbortError
  • Passing a custom http/https Agent through the agent option for keep-alive, self-signed certificates, or custom DNS

How engineers can use it

Install with npm install node-fetch. Import in ESM as import fetch from 'node-fetch'.

GET JSON:

import fetch from 'node-fetch';
const response = await fetch('https://api.github.com/users/github');
const data = await response.json();

POST multipart:

import fetch, { FormData, File } from 'node-fetch';
const formData = new FormData();
const abc = new File([new Uint8Array([97, 98, 99])], 'abc.txt', { type: 'text/plain' });
formData.set('file-upload', abc, 'new name.txt');
const response = await fetch('https://httpbin.org/post', { method: 'POST', body: formData });

Stream a binary response to disk:

import { createWriteStream } from 'node:fs';
import { pipeline } from 'node:stream';
import { promisify } from 'node:util';
import fetch from 'node-fetch';
const streamPipeline = promisify(pipeline);
const response = await fetch('https://github.githubassets.com/images/modules/logos_page/Octocat.png');
if (!response.ok) throw new Error(`unexpected response ${response.statusText}`);
await streamPipeline(response.body, createWriteStream('./octocat.png'));

Documented limitations to plan around: 3xx-5xx responses are not exceptions (check response.ok or throw your own); cookies are not stored automatically and must be pulled from response.headers.raw()['set-cookie']; and the Request class does not implement type, destination, mode, credentials, cache, integrity, or keepalive. v3 cannot be require()d — the README's workaround is const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args)); from CommonJS, or stay on node-fetch@2.