-
Notifications
You must be signed in to change notification settings - Fork 7.7k
add node:http and node:https documentation #24268
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: production
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -7,15 +7,39 @@ import { Render } from "~/components" | |||||
|
||||||
<Render file="nodejs-compat-howto" /> | ||||||
|
||||||
## Compatibility flags | ||||||
|
||||||
### Client-side methods | ||||||
|
||||||
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. | ||||||
|
||||||
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`: | ||||||
|
||||||
```toml | ||||||
compatibility_flags = ["nodejs_compat", "enable_nodejs_http_modules"] | ||||||
``` | ||||||
|
||||||
### Server-side methods | ||||||
|
||||||
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. | ||||||
|
||||||
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`: | ||||||
|
||||||
```toml | ||||||
compatibility_flags = ["nodejs_compat", "enable_nodejs_http_server_modules"] | ||||||
``` | ||||||
|
||||||
To use both client-side and server-side methods, enable both flags: | ||||||
|
||||||
```toml | ||||||
compatibility_flags = ["nodejs_compat", "enable_nodejs_http_modules", "enable_nodejs_http_server_modules"] | ||||||
``` | ||||||
|
||||||
## Agent | ||||||
|
||||||
An implementation of the Node.js [`http.Agent'](https://nodejs.org/docs/latest/api/http.html#class-httpagent) class. | ||||||
An implementation of the Node.js [`http.Agent`](https://nodejs.org/docs/latest/api/http.html#class-httpagent) class. | ||||||
|
||||||
An [Agent](https://nodejs.org/docs/latest/api/http.html#class-httpagent) 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. | ||||||
An [Agent](https://nodejs.org/docs/latest/api/http.html#class-httpagent) 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. | ||||||
|
||||||
```js | ||||||
import { Agent } from 'node:http'; | ||||||
|
@@ -51,13 +75,13 @@ get('http://docs.cloudflare.com/robots.txt', (res) => { | |||||
|
||||||
## request | ||||||
|
||||||
An implementation of the Node.js [`http.request'](https://nodejs.org/docs/latest/api/http.html#httprequesturl-options-callback) method. | ||||||
An implementation of the Node.js [`http.request`](https://nodejs.org/docs/latest/api/http.html#httprequesturl-options-callback) method. | ||||||
|
||||||
The `request` method creates an HTTP request with customizable options like method, headers, and body. It provides full control over the request configuration and returns a [writable stream](https://nodejs.org/docs/latest/api/stream.html#class-streamwritable) for sending request data. | ||||||
|
||||||
```js | ||||||
import { request } from 'node:http'; | ||||||
import { strictEqual } from 'node:assert'; | ||||||
import { strictEqual, ok } from 'node:assert'; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. these examples are, unfortunately, not directly runnable in workers since they show the requests at the top level scope (where we can't perform an i/o). This can be addressed in a follow-up but ideally these examples would show the use of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that would be a good improvement, but it's not really related to this PR which just adds the missing modules and methods. Mind if we track this separately? It's a bigger change that probably deserves its own discussion about how we want to handle the examples and async patterns. Happy to work on it after this gets merged if you want. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My preference would be to improve the examples before it lands but I'll leave it to you. |
||||||
|
||||||
const req = request({ | ||||||
method: 'GET', | ||||||
|
@@ -81,7 +105,10 @@ req.end(); | |||||
``` | ||||||
|
||||||
```js | ||||||
const req = request(new URL('http://docs.cloudflare.com'),{ | ||||||
import { request } from 'node:http'; | ||||||
import { strictEqual } from 'node:assert'; | ||||||
|
||||||
const req = request(new URL('http://docs.cloudflare.com'), { | ||||||
method: 'GET', | ||||||
}, (res) => { | ||||||
// requests to http://docs.cloudflare.com get redirected to their https counterpart. | ||||||
|
@@ -91,7 +118,7 @@ const req = request(new URL('http://docs.cloudflare.com'),{ | |||||
req.end(); | ||||||
``` | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would just remove this second example as it does not really add much value. You can just use text to explain that the input can be an object, URL string, or URL object. |
||||||
|
||||||
The following options passed to the `request` method are not supported due to the differences in the Cloudflare Workers and the implementation of the `node:http` module: | ||||||
The following options passed to the `request` method are not supported due to differences in the Cloudflare Workers implementation of the `node:http` module: | ||||||
- `maxHeaderSize` | ||||||
- `insecureHTTPParser` | ||||||
- `createConnection` | ||||||
|
@@ -100,7 +127,9 @@ The following options passed to the `request` method are not supported due to th | |||||
|
||||||
## OutgoingMessage | ||||||
|
||||||
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 [`Writable` stream class](https://nodejs.org/docs/latest/api/stream.html#class-streamwritable). | ||||||
An implementation of the Node.js [`http.OutgoingMessage`](https://nodejs.org/docs/latest/api/http.html#class-httpoutgoingmessage) class. | ||||||
|
||||||
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). | ||||||
|
||||||
```js | ||||||
import { OutgoingMessage } from 'node:http'; | ||||||
|
@@ -110,9 +139,12 @@ res.writeHead(200, { 'Content-Type': 'text/plain' }); | |||||
res.write('Hello, World!'); | ||||||
res.end(); | ||||||
``` | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given that it would be exceedingly unlikely that the typical use would ever need to create an |
||||||
|
||||||
## IncomingMessage | ||||||
|
||||||
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. | ||||||
An implementation of the Node.js [`http.IncomingMessage`](https://nodejs.org/docs/latest/api/http.html#class-httpincomingmessage) class. | ||||||
|
||||||
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. | ||||||
|
||||||
```js | ||||||
import { get, IncomingMessage } from 'node:http'; | ||||||
|
@@ -123,3 +155,123 @@ get('http://docs.cloudflare.com', (res) => { | |||||
ok(res instanceof IncomingMessage); | ||||||
}); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. import { get, IncomingMessage } from 'node:http';
import { ok, strictEqual } from 'node:assert';
export default {
fetch() {
const { promise, resolve } = Promise.withResolvers();
get('http://docs.cloudflare.com', (res) => {
strictEqual(res.statusCode, 301);
ok(res instanceof IncomingMessage);
resolve(new Response('ok');
});
return promise;
}
} |
||||||
``` | ||||||
|
||||||
The Workers implementation includes a `cloudflare` property on `IncomingMessage` objects: | ||||||
|
||||||
```js | ||||||
import { get } from 'node:http'; | ||||||
|
||||||
get('http://example.com', (res) => { | ||||||
// Access Cloudflare-specific request properties | ||||||
console.log(res.cloudflare.cf.country); | ||||||
console.log(res.cloudflare.cf.ray); | ||||||
}); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the |
||||||
``` | ||||||
|
||||||
The `cloudflare.cf` property contains [Cloudflare-specific request properties](/workers/runtime-apis/request/#incomingrequestcfproperties). | ||||||
|
||||||
The following differences exist between the Workers implementation and Node.js: | ||||||
|
||||||
- Trailer headers are not supported | ||||||
- The `socket` attribute **does not extend from `net.Socket`** and only contains the following properties: `encrypted`, `remoteFamily`, `remoteAddress`, `remotePort`, `localAddress`, `localPort`, and `destroy()` method | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should likely explain that calling |
||||||
|
||||||
## createServer | ||||||
|
||||||
An implementation of the Node.js [`http.createServer`](https://nodejs.org/docs/latest/api/http.html#httpcreateserveroptions-requestlistener) method. | ||||||
|
||||||
The `createServer` method creates an HTTP server instance that can handle incoming requests. It's a convenience function that creates a new `Server` instance and optionally sets up a request listener callback. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
It's not really a "convenience" method. It's the way users are supposed to create servers. The fact that |
||||||
|
||||||
```js | ||||||
import { createServer } from 'node:http'; | ||||||
import { httpServerHandler } from 'cloudflare:node'; | ||||||
|
||||||
const server = createServer((req, res) => { | ||||||
res.writeHead(200, { 'Content-Type': 'text/plain' }); | ||||||
res.end('Hello from Node.js HTTP server!'); | ||||||
}); | ||||||
|
||||||
server.listen(8080); | ||||||
export default httpServerHandler({ port: 8080 }); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The signature and behavior of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that a couple of the details of how |
||||||
``` | ||||||
|
||||||
The `httpServerHandler` function integrates Node.js HTTP servers with the Cloudflare Workers request model. When a request arrives at your Worker, the handler automatically routes it to your Node.js server running on the specified port. This bridge allows you to use familiar Node.js server patterns while benefiting from the Workers runtime environment, including automatic scaling, edge deployment, and integration with other Cloudflare services. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure about this way of explaining it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. The current wording makes it sound like there's an actual server running on a real port, when it's really using the port as a routing key. How would you suggest rewording this to be more accurate? Something along the lines of the handler using the port to determine which app to route to? |
||||||
|
||||||
:::note | ||||||
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: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Users likely aren't going to be familiar with explicit resource management since it is so new. A link here to something that explains it would be most helpful. |
||||||
|
||||||
```js | ||||||
import { createServer } from 'node:http'; | ||||||
|
||||||
await using server = createServer((req, res) => { | ||||||
res.end('Hello World'); | ||||||
}); | ||||||
// Server will be automatically closed when it goes out of scope | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this example, the server would never actually be retained because it's not listening. Also since the |
||||||
``` | ||||||
::: | ||||||
|
||||||
## Server | ||||||
|
||||||
An implementation of the Node.js [`http.Server`](https://nodejs.org/docs/latest/api/http.html#class-httpserver) class. | ||||||
|
||||||
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. | ||||||
|
||||||
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. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's also worth pointing out that passing a port value of |
||||||
|
||||||
```js | ||||||
import { Server } from 'node:http'; | ||||||
import { httpServerHandler } from 'cloudflare:node'; | ||||||
|
||||||
const server = new Server((req, res) => { | ||||||
res.writeHead(200, { 'Content-Type': 'application/json' }); | ||||||
res.end(JSON.stringify({ message: 'Hello from HTTP Server!' })); | ||||||
}); | ||||||
anonrig marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
server.listen(8080); | ||||||
export default httpServerHandler({ port: 8080 }); | ||||||
``` | ||||||
|
||||||
The following differences exist between the Workers implementation and Node.js: | ||||||
|
||||||
- Connection management methods such as `closeAllConnections()` and `closeIdleConnections()` are not implemented | ||||||
- Only `listen()` variants with a port number or no parameters are supported: `listen()`, `listen(0, callback)`, `listen(callback)`, etc. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. General users may not be too familiar with the fact that Node.js' API allows listening on a domain socket, etc so this may be a bit confusing. A link here to the relevant node.js docs would help. |
||||||
- The following server options are not supported: `maxHeaderSize`, `insecureHTTPParser`, `keepAliveTimeout`, `connectionsCheckingInterval` | ||||||
anonrig marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
## ServerResponse | ||||||
|
||||||
An implementation of the Node.js [`http.ServerResponse`](https://nodejs.org/docs/latest/api/http.html#class-httpserverresponse) class. | ||||||
|
||||||
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. | ||||||
|
||||||
```js | ||||||
import { createServer } from 'node:http'; | ||||||
import { httpServerHandler } from 'cloudflare:node'; | ||||||
|
||||||
const server = createServer((req, res) => { | ||||||
ok(res instanceof ServerResponse); | ||||||
|
||||||
// Set multiple headers at once | ||||||
res.writeHead(200, { | ||||||
'Content-Type': 'application/json', | ||||||
'X-Custom-Header': 'Workers-HTTP' | ||||||
}); | ||||||
|
||||||
// Stream response data | ||||||
res.write('{"data": ['); | ||||||
res.write('{"id": 1, "name": "Item 1"},'); | ||||||
res.write('{"id": 2, "name": "Item 2"}'); | ||||||
res.write(']}'); | ||||||
|
||||||
// End the response | ||||||
res.end(); | ||||||
}); | ||||||
anonrig marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
server.listen(8080); | ||||||
export default httpServerHandler({ port: 8080 }); | ||||||
``` | ||||||
|
||||||
The following methods and features are not supported in the Workers implementation: | ||||||
|
||||||
- `assignSocket()` and `detachSocket()` methods are not available | ||||||
anonrig marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
- Trailer headers are not supported | ||||||
- `writeContinue()` and `writeEarlyHints()` methods are not available | ||||||
- 1xx responses in general are not supported |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better to replace this example with a more fully functional one that shows the request used in the correct way (i.e. as a subrequest within a request handler where there is an i/o context, with proper error propagation, a promise that gets resolved for the response, etc).