node:http2 is a separate, Stability 2 – Stable implementation built on nghttp2. Its unit of work is the stream, not the connection: one TCP socket carries many numbered streams at once, headers travel compressed with HPACK, and requests do not block each other. Pseudo-headers (:status, :path) replace the request line.
import { createServer, connect } from 'node:http2';
const server = createServer().on('stream', (s, h) => {
s.respond({ ':status': 200 }); s.end(`you asked for ${h[':path']}`);
});
server.listen(8080, '127.0.0.1', () => {
const client = connect('http://127.0.0.1:8080'); let done = 0;
for (const path of ['/a', '/b', '/c']) {
const stream = client.request({ ':path': path }).setEncoding('utf8');
stream.on('data', (d) => console.log('stream', stream.id, '->', d.trim()));
stream.on('close', () => { if (++done === 3) { client.close(); server.close(); } });
}
});Output
stream 1 -> you asked for /a stream 3 -> you asked for /b stream 5 -> you asked for /c
The ids are 1, 3 and 5 — client-initiated streams are odd-numbered — and all three were open at once. createServer speaks cleartext h2c, for use behind a proxy that has terminated TLS; createSecureServer with key and cert is the public-facing form, where ALPN negotiates h2, and allowHTTP1: true makes it fall back to the compatibility (req, res) API.