Trust is earned, not given

A different perspective

2024-04-23 · Projects

Node.js, part 23: Node 22 — WebSocket client, glob, and magic bytes

Part 23from the Node.js series · 24 parts in all

Node 22 reads like a release that went through the dependency list of an ordinary service and moved half of it into core. A WebSocket client, a glob implementation, a file watcher, and the node --run script runner all shipped — each one retiring a very popular package. Part 23 is what they replace and how to use them.

A WebSocket client, no ws dependency

const ws = new WebSocket('wss://example.com/feed');

ws.onopen = () => ws.send(JSON.stringify({ subscribe: 'prices' }));
ws.onmessage = (event) => console.log(JSON.parse(event.data));
ws.onerror = (err) => console.error('socket error', err.message);

// Closing cleanly, which is the part hand-rolled clients get wrong.
ws.close(1000, 'done');

The API is the browser's, which is the same argument as fetch in part 16: one piece of knowledge, two runtimes. Reconnection, backoff, and heartbeats are still yours — a socket library is not a resilience library.

glob and watch, in core

const { glob } = require('node:fs/promises');
const { watch } = require('node:fs');

const tests = await glob('src/**/*.{test,spec}.{js,ts}');

// Cross-platform recursive watch: no chokidar, no fsevents build step.
for await (const event of watch('src', { recursive: true })) {
  console.log(event.eventType, event.filename);
}

That second block is the one that removes a native dependency from a lot of toolchains — chokidar existed largely because recursive watching was unreliable, and the platform support is now good enough for core to expose it.

Smaller things worth knowing

Across parts 16, 22 and 23 the pattern is the same: the platform absorbed the packages everyone was installing, and the reasonable dependency list for a Node service became visibly shorter than it was in 2018. Next: the last dependency worth removing — the one that runs your logging and shutdown.