tl;dr: Let your users handle it. See https://github.com/BuilderIO/this-package-uses-fetch/
And when it comes to fetching data, there are 2 standards provided by browsers:
We won’t dive too deep into the differences between these two, as this post is focused on how to use
fetch(), not how to decide between
XMLHttpRequest! So we’ll assume you went with
So you’ve chosen and started using
fetch() in your code, making calls like
fetch('https://example.com'). Great! This works in most modern browsers:
But as the above chart shows, around ~4% of internet users today might still be relying on browsers that do not support
fetch. What can you do for them?
Before jumping to conclusions, make sure that you actually have users in non-fetch environments (e.g. by importing your analytics into caniuse and looking at the browser usage of your users). It might be the case that you can actually get away with not doing anything at all!
If it turns out that you do have users on older browsers, then the way to fix this is to provide a fetch polyfill to support them:
In this case, Github offers a great fetch polyfill for browsers: https://github.com/github/fetch
fetch support until v18 (released in April 2022).
So for all versions of Node.js ≤17, you’ll need a Node fetch polyfill, the most popular of which is
As you can tell, many of the newer runtimes provide Fetch support from the get-go.
What would happen if you, the author, added these polyfills to your code?
You’d start by installing
node-fetch as a package in
dependencies, maybe include the GitHub polyfill for browsers too, and finally include some logic that toggles between server and browser polyfills. It might look something like the following:
The nice thing about implementing this toggle is that, if implemented just right, your users won’t have to worry about a thing: they can
npm install your-cool-library and start using it with no extra steps.
However, this approach runs into a host of issues & dilemmas:
node-fetchis now a hard dependency in your app. Since it depends on certain Node.js APIs, it might cause issues if it (accidentally or otherwise) gets imported in other server runtimes like Cloudflare Workers (which do not support these).
node-fetch) for another purpose, you’d be forcing them to needlessly have 2 versions of the same package.
node-fetchv3 is ESM-only and does not work in CommonJS projects. You need to either stay with v2 to support both or choose for your library to be ESM-only as well.
You can get around some of the above issues using a library called isomorphic-fetch, but not all of them. It also hasn’t been updated since 2015, and is locked on v2.x of node-fetch (whereas v3 has been out for some time now).
As much as we’d prefer the “we got you covered” approach of implementing polyfills on behalf of our users, it is too painful and brittle to do it this way.
Instead, as a general rule, I argue that you (a proud npm package author) should not include polyfills in your published code or dependency tree. You should leave it up to your users to do as they see fit, based on their needs & environments. This guarantees that:
fetch()instead of importing a wrapped
With Node v16 reaching end-of-life in Fall 2023, we are quickly approaching a time when polyfilling
fetch() is far less important. But we are probably still years away from most Node users updating to v18, so we still need to provide clear documentation on how to polyfill fetch. This is why we created this github README: https://github.com/BuilderIO/this-package-uses-fetch
It includes a summary of the above, with clear actionable steps for each environment. The goal is to avoid the need for all package authors to explain the same thing over and over again. In fact, we just started using it ourselves in our SDK code: https://github.com/BuilderIO/builder/pull/1661
We invite other folks who use
fetch to link to this document in their READMEs, and contribute any ideas they have!