I think it's fair to expect divergence with an API that was made for browsers being mimicked elsewhere; here it's more about faithfulness and predictability
node-fetch, cross-fetch, undici fetch, deno fetch, etc. these are all faithful to the original API, and behave in ways that wouldn't surprise you if you're familiar with fetch. the more unfamiliar nooks and crannies of the API (e.g. ReadableStream) is sort of a rats nest unfortunately 😅 but that's beside the point
here's the difference with Next.js: now I'm not writing fetch with fetch in mind, I'm writing fetch with Next.js's caching behaviors in mind, and a hidden fetch call multiple levels down in the stack can unexpectedly change that behavior in potentially unexpected ways
happy to be convinced otherwise though. I know y'all give this a lot of thought, but this was just really out of left field for me 🫠
It's important that it's the same
fetch()
api so that you can actually use existing libraries from anywhere. It can't be a custom one or it would defeat the purpose. There would be too many libraries that callfetch()
and cause mistakes. There would be no point. If anything the alternative would be to error for any library that uses plain fetch and redirect users to something that doesn't.Note that there are a lot of Node.js libraries that already add deduping because it's such a common problem already. It's easy to make the mistake that you fetch it again. One alternative would be to just recommend everyone use one of those but what's the point then?
There are a lot of bailout cases, including mutative verbs. Really, for almost anything you should be implementing an AbortSignal to fetch. If you do, then it doesn't kick in and respects your own life time.
For almost all scenarios the dedupe behavior is something browsers and other environments already implement. E.g. a browser will not issue a duplicate fetch request until the first response comes back - and only if that response comes back specifically with a
Cache-control: no-store
header will it even issue the second request. The issue is that those cache headers are meant to communicate that it shouldn't store it long lived like on disk. It doesn't actually understand the semantic domain specific knowledge of the request life time of an SSR render. So it's only in that specific scenario that it needs to cache it in memory - a little longer - but only for the lifetime of that SSR render and not shared across incoming requests.I'd consider taking a look at what scenario that you're concerned about or trying to find bugs in real code and report back.