Networking#

Modern JavaScript runtimes (Node, Deno, Bun) and browsers share the same fetch API for HTTP, plus runtime-specific lower-level APIs.

fetch#

The standard HTTP client; works in browsers, Deno, Bun, and Node 18+.

const res = await fetch("https://api.example.com/users/1");
if (!res.ok) {
  throw new Error(`HTTP ${res.status}`);
}
const user = await res.json();

POST with JSON.

await fetch("https://api.example.com/users", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ name: "operator" }),
});

Streaming, timeouts, and cancellation use AbortController:

const ac = new AbortController();
setTimeout(() => ac.abort(), 5000);

const res = await fetch(url, { signal: ac.signal });
for await (const chunk of res.body) {
  process(chunk);
}

Node HTTP Modules#

import http from "node:http";

const server = http.createServer((req, res) => {
  res.writeHead(200, { "content-type": "application/json" });
  res.end(JSON.stringify({ ok: true }));
});
server.listen(8080);

For HTTPS use node:https; for HTTP/2 use node:http2.

TCP / UDP / Unix Sockets#

import net from "node:net";

const server = net.createServer((conn) => {
  conn.write("hello\n");
  conn.on("data", (b) => conn.write(b));
}).listen(9000);

const client = net.createConnection({ port: 9000 }, () => {
  client.write("hi");
});

UDP via node:dgram; Unix domain sockets by passing a path option to net.createServer / net.createConnection.

WebSockets#

In browsers.

const ws = new WebSocket("wss://example.com/ws");
ws.addEventListener("message", (e) => console.log(e.data));
ws.send("hello");

In Node 22+ WebSocket is built in. On older Node, use:

  • ws, the standard library.

  • Browser API also available in undici.

Server-Sent Events#

Lighter than WebSockets when the server only needs to push.

// server
res.writeHead(200, {
  "content-type": "text/event-stream",
  "cache-control": "no-cache",
  "connection": "keep-alive",
});
res.write("data: hello\n\n");

// client
const es = new EventSource("/stream");
es.onmessage = (e) => console.log(e.data);

Higher-Level HTTP Clients#

  • axios , still common; Promise-based, interceptors.

  • undici , the high-performance Node HTTP client (fetch under the hood in Node).

  • got, ergonomic Node-only client.

  • ofetch , tiny fetch wrapper.

DNS#

In Node.

import dns from "node:dns/promises";

const addrs = await dns.resolve4("example.com");
const ptr   = await dns.reverse("8.8.8.8");
const srv   = await dns.resolveSrv("_xmpp._tcp.example.com");

TLS / mTLS#

import https from "node:https";

const agent = new https.Agent({
  ca: fs.readFileSync("ca.pem"),
  cert: fs.readFileSync("client.pem"),
  key:  fs.readFileSync("client-key.pem"),
});

const res = await fetch("https://internal/", { dispatcher: undici_agent });

Servers / Frameworks#

For real applications, see Frameworks (Express, Fastify, Hono, Koa, NestJS). The standard library is fine for tiny services and prototypes; a framework saves work for everything else.

Pitfalls#

  • No default timeout on ``fetch``, always pair with an AbortController.

  • Headers are case-insensitive, but use lowercase to be safe.

  • ``fetch`` doesn’t throw on 4xx / 5xx, check res.ok yourself.

  • Reading a Response twice is an error, clone() first if needed.

  • Node TLS doesn’t use the system CA store on macOS / Windows by default, use --use-system-ca (Node 22+) or set NODE_EXTRA_CA_CERTS.