Skip to content

Commit 7b3eb4e

Browse files
committed
add node:http and node:https documentation
1 parent e8c917e commit 7b3eb4e

File tree

3 files changed

+389
-140
lines changed

3 files changed

+389
-140
lines changed

src/content/docs/workers/runtime-apis/nodejs/http.mdx

Lines changed: 247 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,38 @@ pcx_content_type: configuration
33
title: http
44
---
55

6-
import { Render } from "~/components"
6+
import { Render } from "~/components";
77

88
<Render file="nodejs-compat-howto" />
99

10+
## Compatibility flags
11+
12+
### Client-side methods
13+
14+
To use the HTTP client-side methods (`http.get`, `http.request`, etc.), you must enable the [`enable_nodejs_http_modules`](/workers/configuration/compatibility-flags/) compatibility flag in addition to the [`nodejs_compat`](/workers/runtime-apis/nodejs/) flag.
15+
16+
This flag is automatically enabled for Workers using a [compatibility date](/workers/configuration/compatibility-dates/) of `2025-08-15` or later when `nodejs_compat` is enabled. For Workers using an earlier compatibility date, you can manually enable it by adding the flag to your `wrangler.toml`:
17+
18+
```toml
19+
compatibility_flags = ["nodejs_compat", "enable_nodejs_http_modules"]
20+
```
21+
22+
### Server-side methods
23+
24+
To use the HTTP server-side methods (`http.createServer`, `http.Server`, `http.ServerResponse`), you must enable the `enable_nodejs_http_server_modules` compatibility flag in addition to the [`nodejs_compat`](/workers/runtime-apis/nodejs/) flag.
25+
26+
This flag is automatically enabled for Workers using a [compatibility date](/workers/configuration/compatibility-dates/) of `2025-09-01` or later when `nodejs_compat` is enabled. For Workers using an earlier compatibility date, you can manually enable it by adding the flag to your `wrangler.toml`:
27+
28+
```toml
29+
compatibility_flags = ["nodejs_compat", "enable_nodejs_http_server_modules"]
30+
```
31+
32+
To use both client-side and server-side methods, enable both flags:
33+
34+
```toml
35+
compatibility_flags = ["nodejs_compat", "enable_nodejs_http_modules", "enable_nodejs_http_server_modules"]
36+
```
37+
1038
## get
1139

1240
An implementation of the Node.js [`http.get`](https://nodejs.org/docs/latest/api/http.html#httpgetoptions-callback) method.
@@ -18,25 +46,25 @@ fetch or similar handler. Outside of such a handler, attempts to use `get` will
1846
an error.
1947

2048
```js
21-
import { get } from 'node:http';
49+
import { get } from "node:http";
2250

2351
export default {
2452
async fetch() {
2553
const { promise, resolve, reject } = Promise.withResolvers();
26-
get('http://example.org', (res) => {
27-
let data = '';
28-
res.setEncoding('utf8');
29-
res.on('data', (chunk) => {
54+
get("http://example.org", (res) => {
55+
let data = "";
56+
res.setEncoding("utf8");
57+
res.on("data", (chunk) => {
3058
data += chunk;
3159
});
32-
res.on('end', () => {
60+
res.on("end", () => {
3361
resolve(new Response(data));
3462
});
35-
res.on('error', reject);
36-
}).on('error', reject);
63+
res.on("error", reject);
64+
}).on("error", reject);
3765
return promise;
38-
}
39-
}
66+
},
67+
};
4068
```
4169

4270
The implementation of `get` in Workers is a wrapper around the global
@@ -58,31 +86,35 @@ fetch or similar handler. Outside of such a handler, attempts to use `request` w
5886
an error.
5987

6088
```js
61-
import { get } from 'node:http';
89+
import { get } from "node:http";
6290

6391
export default {
6492
async fetch() {
6593
const { promise, resolve, reject } = Promise.withResolvers();
66-
get({
67-
method: 'GET',
68-
protocol: 'http:',
69-
hostname: 'example.org',
70-
path: '/'
71-
}, (res) => {
72-
let data = '';
73-
res.setEncoding('utf8');
74-
res.on('data', (chunk) => {
75-
data += chunk;
76-
});
77-
res.on('end', () => {
78-
resolve(new Response(data));
79-
});
80-
res.on('error', reject);
81-
}).on('error', reject)
82-
.end();
94+
get(
95+
{
96+
method: "GET",
97+
protocol: "http:",
98+
hostname: "example.org",
99+
path: "/",
100+
},
101+
(res) => {
102+
let data = "";
103+
res.setEncoding("utf8");
104+
res.on("data", (chunk) => {
105+
data += chunk;
106+
});
107+
res.on("end", () => {
108+
resolve(new Response(data));
109+
});
110+
res.on("error", reject);
111+
},
112+
)
113+
.on("error", reject)
114+
.end();
83115
return promise;
84-
}
85-
}
116+
},
117+
};
86118
```
87119

88120
The following options passed to the `request` (and `get`) method are not supported due to the differences required by Coudflare Workers implementation of `node:http` as a wrapper around the global `fetch` API:
@@ -97,26 +129,17 @@ The following options passed to the `request` (and `get`) method are not support
97129

98130
The [`OutgoingMessage`](https://nodejs.org/docs/latest/api/http.html#class-httpoutgoingmessage) class represents an HTTP response that is sent to the client. It provides methods for writing response headers and body, as well as for ending the response. `OutgoingMessage` extends from the Node.js [`stream.Writable` stream class](https://developers.cloudflare.com/workers/runtime-apis/nodejs/streams/).
99131

100-
```js
101-
import { OutgoingMessage } from 'node:http';
132+
The `OutgoingMessage` class is a base class for outgoing HTTP messages (both requests and responses). It provides methods for writing headers and body data, as well as for ending the message. `OutgoingMessage` extends from the [`Writable` stream class](https://nodejs.org/docs/latest/api/stream.html#class-streamwritable).
102133

103-
export default {
104-
async fetch() {
105-
// ...
106-
const res = new OutgoingMessage();
107-
res.writeHead(200, { 'Content-Type': 'text/plain' });
108-
res.write('Hello, World!');
109-
res.end();
110-
// ...
111-
}
112-
}
113-
```
134+
Both `ClientRequest` and `ServerResponse` both extend from and inherit from `OutgoingMessage`.
114135

115136
## IncomingMessage
116137

117138
The `IncomingMessage` class represents an HTTP request that is received from the client. It provides methods for reading request headers and body, as well as for ending the request. `IncomingMessage` extends from the `Readable` stream class.
118139

119-
```js
140+
The `IncomingMessage` class represents an HTTP message (request or response). It provides methods for reading headers and body data. `IncomingMessage` extends from the `Readable` stream class.
141+
142+
````js
120143
import { get, IncomingMessage } from 'node:http';
121144
import { ok, strictEqual } from 'node:assert';
122145

@@ -129,7 +152,37 @@ export default {
129152
// ...
130153
}
131154
}
132-
```
155+
156+
The Workers implementation includes a `cloudflare` property on `IncomingMessage` objects:
157+
158+
```js
159+
import { createServer } from "node:http";
160+
import { httpServerHandler } from "cloudflare:node";
161+
162+
const server = createServer((req, res) => {
163+
console.log(req.cloudflare.cf.country);
164+
console.log(req.cloudflare.cf.ray);
165+
res.write("Hello, World!");
166+
res.end();
167+
});
168+
169+
server.listen(8080);
170+
171+
export default httpServerHandler({ port: 8080 });
172+
````
173+
174+
The `cloudflare.cf` property contains [Cloudflare-specific request properties](/workers/runtime-apis/request/#incomingrequestcfproperties).
175+
176+
The following differences exist between the Workers implementation and Node.js:
177+
178+
- Trailer headers are not supported
179+
- The `socket` attribute **does not extend from `net.Socket`** and only contains the following properties: `encrypted`, `remoteFamily`, `remoteAddress`, `remotePort`, `localAddress`, `localPort`, and `destroy()` method.
180+
- The following `socket` attributes behave differently than their Node.js counterparts:
181+
- `remoteAddress` will return `127.0.0.1` when ran locally
182+
- `remotePort` will return a random port number between 2^15 and 2^16
183+
- `localAddress` will return the value of request's `host` header if exists. Otherwise, it will return `127.0.0.1`
184+
- `localPort` will return the port number assigned to the server instance
185+
- `req.socket.destroy()` falls through to `req.destroy()`
133186
134187
## Agent
135188
@@ -138,37 +191,161 @@ A partial implementation of the Node.js [`http.Agent'](https://nodejs.org/docs/l
138191
An `Agent` manages HTTP connection reuse by maintaining request queues per host/port. In the workers environment, however, such low-level management of the network connection, ports, etc, is not relevant because it is handled by the Cloudflare infrastructure instead. Accordingly, the implementation of `Agent` in Workers is a stub implementation that does not support connection pooling or keep-alive.
139192
140193
```js
141-
import { get, Agent } from 'node:http';
142-
import { strictEqual } from 'node:assert';
194+
import { Agent } from "node:http";
195+
import { strictEqual } from "node:assert";
196+
197+
const agent = new Agent();
198+
strictEqual(agent.protocol, "http:");
199+
```
200+
201+
## createServer
202+
203+
An implementation of the Node.js [`http.createServer`](https://nodejs.org/docs/latest/api/http.html#httpcreateserveroptions-requestlistener) method.
204+
205+
The `createServer` method creates an HTTP server instance that can handle incoming requests.
206+
207+
```js
208+
import { createServer } from "node:http";
209+
import { httpServerHandler } from "cloudflare:node";
210+
211+
const server = createServer((req, res) => {
212+
res.writeHead(200, { "Content-Type": "text/plain" });
213+
res.end("Hello from Node.js HTTP server!");
214+
});
215+
216+
server.listen(8080);
217+
export default httpServerHandler({ port: 8080 });
218+
```
219+
220+
## Node.js integration
221+
222+
### httpServerHandler
223+
224+
The `httpServerHandler` function integrates Node.js HTTP servers with the Cloudflare Workers request model. It supports two API patterns:
225+
226+
```js
227+
import http from "node:http";
228+
import { httpServerHandler } from "cloudflare:node";
229+
230+
const server = http.createServer((req, res) => {
231+
res.end("hello world");
232+
});
233+
234+
// Pass server directly (simplified) - automatically calls listen() if needed
235+
export default httpServerHandler(server);
236+
237+
// Or use port-based routing for multiple servers
238+
server.listen(8080);
239+
export default httpServerHandler({ port: 8080 });
240+
```
241+
242+
The handler automatically routes incoming Worker requests to your Node.js server. When using port-based routing, the port number acts as a routing key to determine which server handles requests, allowing multiple servers to coexist in the same Worker.
243+
244+
### handleAsNodeRequest
245+
246+
For more direct control over request routing, you can use the `handleAsNodeRequest` function from `cloudflare:node`. This function directly routes a Worker request to a Node.js server running on a specific port:
247+
248+
```js
249+
import { createServer } from "node:http";
250+
import { handleAsNodeRequest } from "cloudflare:node";
251+
252+
const server = createServer((req, res) => {
253+
res.writeHead(200, { "Content-Type": "text/plain" });
254+
res.end("Hello from Node.js HTTP server!");
255+
});
256+
257+
server.listen(8080);
143258
144259
export default {
145-
async fetch() {
260+
fetch(request) {
261+
return handleAsNodeRequest(8080, request);
262+
},
263+
};
264+
```
146265
147-
const agent = new Agent();
148-
get({
149-
hostname: 'example.org',
150-
port: 80,
151-
path: '/',
152-
agent, // possible but not all that useful.
153-
}, (res) => {
154-
// ...
155-
});
266+
This approach gives you full control over the fetch handler while still leveraging Node.js HTTP servers for request processing.
156267
157-
return new Response('ok');
158-
}
159-
}
268+
:::note
269+
Failing to call `close()` on an HTTP server may result in the server persisting until the worker is destroyed. In most cases, this is not an issue since servers typically live for the lifetime of the worker. However, if you need to create multiple servers during a worker's lifetime or want explicit lifecycle control (such as in test scenarios), call `close()` when you're done with the server, or use [explicit resource management](https://v8.dev/features/explicit-resource-management).
270+
:::
271+
272+
## Server
273+
274+
An implementation of the Node.js [`http.Server`](https://nodejs.org/docs/latest/api/http.html#class-httpserver) class.
275+
276+
The `Server` class represents an HTTP server and provides methods for handling incoming requests. It extends the Node.js `EventEmitter` class and can be used to create custom server implementations.
277+
278+
When using `httpServerHandler`, the port number specified in `server.listen()` acts as a routing key rather than an actual network port. The handler uses this port to determine which HTTP server instance should handle incoming requests, allowing multiple servers to coexist within the same Worker by using different port numbers for identification. Using a port value of `0` (or `null` or `undefined`) will result in a random port number being assigned.
279+
280+
```js
281+
import { Server } from "node:http";
282+
import { httpServerHandler } from "cloudflare:node";
283+
284+
const server = new Server((req, res) => {
285+
res.writeHead(200, { "Content-Type": "application/json" });
286+
res.end(JSON.stringify({ message: "Hello from HTTP Server!" }));
287+
});
288+
289+
server.listen(8080);
290+
export default httpServerHandler({ port: 8080 });
160291
```
161292
293+
The following differences exist between the Workers implementation and Node.js:
294+
295+
- Connection management methods such as `closeAllConnections()` and `closeIdleConnections()` are not implemented
296+
- Only `listen()` variants with a port number or no parameters are supported: `listen()`, `listen(0, callback)`, `listen(callback)`, etc. For reference, see the [Node.js documentation](https://nodejs.org/docs/latest/api/net.html#serverlisten).
297+
- The following server options are not supported: `maxHeaderSize`, `insecureHTTPParser`, `keepAliveTimeout`, `connectionsCheckingInterval`
298+
299+
## ServerResponse
300+
301+
An implementation of the Node.js [`http.ServerResponse`](https://nodejs.org/docs/latest/api/http.html#class-httpserverresponse) class.
302+
303+
The `ServerResponse` class represents the server-side response object that is passed to request handlers. It provides methods for writing response headers and body data, and extends the Node.js `Writable` stream class.
304+
305+
```js
306+
import { createServer, ServerResponse } from "node:http";
307+
import { httpServerHandler } from "cloudflare:node";
308+
import { ok } from "node:assert";
309+
310+
const server = createServer((req, res) => {
311+
ok(res instanceof ServerResponse);
312+
313+
// Set multiple headers at once
314+
res.writeHead(200, {
315+
"Content-Type": "application/json",
316+
"X-Custom-Header": "Workers-HTTP",
317+
});
318+
319+
// Stream response data
320+
res.write('{"data": [');
321+
res.write('{"id": 1, "name": "Item 1"},');
322+
res.write('{"id": 2, "name": "Item 2"}');
323+
res.write("]}");
324+
325+
// End the response
326+
res.end();
327+
});
328+
329+
export default httpServerHandler(server);
330+
```
331+
332+
The following methods and features are not supported in the Workers implementation:
333+
334+
- `assignSocket()` and `detachSocket()` methods are not available
335+
- Trailer headers are not supported
336+
- `writeContinue()` and `writeEarlyHints()` methods are not available
337+
- 1xx responses in general are not supported
338+
162339
## Other differences between Node.js and Workers implementation of `node:http`
163340
164341
Because the Workers implementation of `node:http` is a wrapper around the global `fetch` API, there are some differences in behavior and limitations compared to a standard Node.js environment:
165342
166-
* `Connection` headers are not used. Workers will manage connections automatically.
167-
* `Content-Length` headers will be handled the same way as in the `fetch` API. If a body is provided, the header will be set automatically and manually set values will be ignored.
168-
* `Expect: 100-continue` headers are not supported.
169-
* Trailing headers are not supported.
170-
* The `'continue'` event is not supported.
171-
* The `'information'` event is not supported.
172-
* The `'socket'` event is not supported.
173-
* The `'upgrade'` event is not supported.
174-
* Gaining direct access to the underlying `socket` is not supported.
343+
- `Connection` headers are not used. Workers will manage connections automatically.
344+
- `Content-Length` headers will be handled the same way as in the `fetch` API. If a body is provided, the header will be set automatically and manually set values will be ignored.
345+
- `Expect: 100-continue` headers are not supported.
346+
- Trailing headers are not supported.
347+
- The `'continue'` event is not supported.
348+
- The `'information'` event is not supported.
349+
- The `'socket'` event is not supported.
350+
- The `'upgrade'` event is not supported.
351+
- Gaining direct access to the underlying `socket` is not supported.

0 commit comments

Comments
 (0)