nodebestpractices icon indicating copy to clipboard operation
nodebestpractices copied to clipboard

Prefer cross-platform API when possible

Open jimmywarting opened this issue 3 years ago • 6 comments

  • Prefer to use Uint8Array over Buffer whenever possible - bundle buffer into browser is not that good of idea
    • Buffer is bloated with something DataView, TextEncoder, TextDecoder is suppose to handle
    • Use ArrayBuffer.isView or instancesof Uint8Array checks instead of using Buffer.isBuffer
  • Use the Spec'ed EventTarget instead of EventEmitter (again Deno and browser don't have EventEmitter)
  • To be Isomorphic across whatwg stream and node streams, prefer async iterator instead, this don't require you to import the hole node:stream package into browser and you will be more acceptable of more formats such as async generators that are not only limited to streams. If you are using for await (let chunk of iterable) then you can handle node:streams whatwg streams and any simple generator the same way
  • Don't use node's Url or querystring
    • The WHATWG URL Standard uses a more selective and fine grained approach to selecting encoded characters than that used by the Legacy API.
    • WHATWG URL and URLSearchParams is available in more environments
    • querystring will mix the value between string and arrays giving you an inconsistent api
  • Don't use node's string_decoder, use TextDecoder instead (same goes for encoder)
  • Don't use util.inherits use extend instead

jimmywarting avatar Jun 18 '21 13:06 jimmywarting

Thank you very much! I think the title should be: Prefer cross-platform API when possible

rluvaton avatar Jun 18 '21 14:06 rluvaton

This ideas comes from https://github.com/cross-js/cross-js

jimmywarting avatar Jun 18 '21 21:06 jimmywarting

@jimmywarting Interesting 💜

This looks like a great BP candidate. Let's clarify the point and the TOC first before writing.

Is this focused only on package creators or also custom backend code as well (e.g. something that is anyway making use of Backend-ish narratives like web server, file system and DB)?

Any downsides/exclusions (e.g. performance)?

I remember (but need to refresh my memory) that Buffer is doing some unique optimizations, since Buffer inherits from Uint8Array, would it make sense to use the Uint8Array "interface" but using some simple factory/DI instantiate the Buffer in the backend (fully compatible with frontend)?

goldbergyoni avatar Jun 21 '21 08:06 goldbergyoni

Hopefully the streams part can be updated soon™️ https://github.com/nodejs/node/pull/39062

AdriVanHoudt avatar Jun 21 '21 09:06 AdriVanHoudt

Capturing one more thought here - Beyond platformatbility, one more reason to opt for the universal syntax is due to popularity. With time, EventTarget, async iterators and others will become much more familiar to developers. Mostly full-stack developers

goldbergyoni avatar Jun 21 '21 09:06 goldbergyoni

I remember that Buffer is doing some unique optimizations

It's true that Node buffer dose some optimisation to the Buffer

like

  • re-using previous allocated (unused/forgotten) chunk of arrayBuffer.
  • some speed optimisation when eg converting something to hex using .toString('hex')
  • and buffer.slice acting more like uint8array.subarray
    • i consider this as something bad/unsafe. When i need something to be immutable like in fetch's body or blob's blobParts then you shouldn't be able to modify it anymore, slice dose something unexpected to end user. If you really intended to use the same underlying arrayBuffer with a other set then you should be using subarray instead.

Buffer was invented a long time ago before we had grate support for DataView, TextEncoder, TextDecoder, and any TypedArray, Now days it's just mostly bloated and packed with a lot of things you don't use

For a small package like https://github.com/fb55/bitfield that do not need any fancy Buffer utilities i notice that it used less memory when i switched it to a Uint8Array. and it also run much faster.

I assume a Uint8Array is also much easier to GC than a what a Buffer is

Is this focused only on package creators or also custom backend code as well (e.g. something that is anyway making use of Backend-ish narratives like web server, file system and DB)?

My cross-js guide lines is for anyone that wants to build packages meant to run on multiple platform like on Deno, Browser and Node. An example of that could be Dropbox SDK. Even if you don't build for cross platform modules then there is going to be someone else who wants to use xyz that dose most of the things you need in the browser but are incapable of doing so cuz it depends on fs on the backend for example

NodeJS is starting to align more with Web standards shipping more and more cross spec'ed apis https://www.youtube.com/watch?v=6EDaayYnw6M Ryhan dhal (creator of node) focus on this much more in Deno when started from a new clean slate

jimmywarting avatar Jun 21 '21 10:06 jimmywarting