-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Adding changelog for node compat improvements #19341
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
Changes from all commits
b3dc231
55213cc
ae95ec6
a388562
9158bc0
00e240e
b52cf38
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 |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| --- | ||
| title: Support for Node.js DNS, Net, and Timer APIs in Workers | ||
| description: Node.js APIs from the node:dns, node:net, and node:timers modules are now available when using nodejs_compat. | ||
| products: | ||
| - workers | ||
| date: 2025-01-28T13:00:00Z | ||
| --- | ||
|
|
||
| import { Render, PackageManagers, TypeScriptExample } from "~/components"; | ||
|
|
||
| When using a Worker with the [`nodejs_compat`](/workers/runtime-apis/nodejs/) compatibility flag enabled, you can now use the following Node.js APIs: | ||
|
|
||
| - [`node:net`](/workers/runtime-apis/nodejs/net/) | ||
| - [`node:dns`](/workers/runtime-apis/nodejs/dns/) | ||
| - [`node:timers`](/workers/runtime-apis/nodejs/timers/) | ||
|
|
||
| #### node:net | ||
|
|
||
| This makes it possible to connect to databases such as [MySQL](https://www.mysql.com/), [PostgreSQL](https://www.postgresql.org/), | ||
| [Redis](https://redis.io/), or [MongoDB](https://github.com/mongodb/mongo). Though for production use, we recommend that you connect | ||
| using TLS, which can be done with the [`cloudflare:sockets`](/workers/runtime-apis/tcp-sockets/#connect) | ||
| module, or prefereably by using [Hyperdrive](/hyperdrive), which includes | ||
| [connection pooling](/hyperdrive/configuration/how-hyperdrive-works/#connection-pooling) | ||
| and [query caching](/hyperdrive/configuration/how-hyperdrive-works/#query-caching). | ||
|
|
||
| <TypeScriptExample filename="index.ts"> | ||
| ```ts | ||
| import net from "node:net"; | ||
|
|
||
| const exampleIP = "127.0.0.1"; | ||
|
|
||
| export default { | ||
| async fetch(req): Promise<Response> { | ||
| const socket = new net.Socket(); | ||
| socket.connect(4000, exampleIP, function () { | ||
| console.log("Connected"); | ||
| }); | ||
|
|
||
| socket.write("Hello, Server!"); | ||
| socket.end(); | ||
|
|
||
| return new Response("Wrote to server", { status: 200 }); | ||
| }, | ||
| } satisfies ExportedHandler; | ||
| ```` | ||
| </TypeScriptExample> | ||
|
|
||
| Additionally, you can now use other APIs incliding [`net.BlockList`](https://nodejs.org/api/net.html#class-netblocklist) and | ||
| [`net.SocketAddress`](https://nodejs.org/api/net.html#class-netsocketaddress). | ||
|
|
||
| Note that [`net.Server`](https://nodejs.org/api/net.html#class-netserver) is not supported. | ||
|
|
||
| #### node:dns | ||
|
|
||
| You can use [`node:dns`](https://nodejs.org/api/dns.html) for name resolution via [DNS over HTTPS](/1.1.1.1/encryption/dns-over-https/) using | ||
| [Cloudflare DNS](https://www.cloudflare.com/application-services/products/dns/) at 1.1.1.1. | ||
|
|
||
| <TypeScriptExample filename="index.ts"> | ||
| ```ts | ||
| import dns from "node:dns"; | ||
|
|
||
| dns.lookup("example.org", (_err: any, address: string, ipFamily: number) => | ||
| console.log(`address: ${address} family: IPv${ipFamily}`)); | ||
| ```` | ||
|
|
||
| </TypeScriptExample> | ||
|
|
||
| All `node:dns` functions are available, except `lookup`, `lookupService`, and `resolve` which throw "Not implemented" errors when called. | ||
|
|
||
| #### node:timers | ||
|
|
||
| You can use [`node:timers`](https://nodejs.org/api/timers.html) to schedule functions to be called at some future period of time. | ||
|
|
||
| This includes [`setTimeout`](https://nodejs.org/api/timers.html#settimeoutcallback-delay-args) for calling a function after a delay, | ||
| [`setInterval`](https://nodejs.org/api/timers.html#setintervalcallback-delay-args) for calling a function repeatedly, | ||
| and [`setImmediate`](https://nodejs.org/api/timers.html#setimmediatecallback-args) for calling a function in the next iteration of the event loop. | ||
|
|
||
| <TypeScriptExample filename="index.ts"> | ||
| ```ts | ||
| import timers from "node:timers"; | ||
|
|
||
| console.log("first"); | ||
| timers.setTimeout(() => { | ||
| console.log("last"); | ||
| }, 10); | ||
|
|
||
| timers.setTimeout(() => { | ||
| console.log("next"); | ||
| }); | ||
| ``` | ||
| </TypeScriptExample> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,39 +1,35 @@ | ||
| --- | ||
| pcx_content_type: configuration | ||
| title: assert | ||
|
|
||
| --- | ||
|
|
||
| import { Render } from "~/components" | ||
| import { Render } from "~/components"; | ||
|
|
||
| <Render file="nodejs-compat-howto" /> | ||
|
|
||
| The `assert` module in Node.js provides a number of useful assertions that are useful when building tests. | ||
|
|
||
| ```js | ||
| import { | ||
| strictEqual, | ||
| deepStrictEqual, | ||
| ok, | ||
| doesNotReject, | ||
| } from 'node:assert'; | ||
| import { strictEqual, deepStrictEqual, ok, doesNotReject } from "node:assert"; | ||
|
|
||
| strictEqual(1, 1); // ok! | ||
| strictEqual(1, "1"); // fails! throws AssertionError | ||
|
|
||
| deepStrictEqual({ a: { b: 1 }}, { a: { b: 1 }});// ok! | ||
| deepStrictEqual({ a: { b: 1 }}, { a: { b: 2 }});// fails! throws AssertionError | ||
| deepStrictEqual({ a: { b: 1 } }, { a: { b: 1 } }); // ok! | ||
| deepStrictEqual({ a: { b: 1 } }, { a: { b: 2 } }); // fails! throws AssertionError | ||
|
|
||
| ok(true); // ok! | ||
| ok(false); // fails! throws AssertionError | ||
|
|
||
| await doesNotReject(async () => {}); // ok! | ||
| await doesNotReject(async () => { throw new Error('boom') }); // fails! throws AssertionError | ||
| await doesNotReject(async () => { | ||
| throw new Error("boom"); | ||
| }); // fails! throws AssertionError | ||
| ``` | ||
|
|
||
| :::note | ||
|
|
||
| In the Workers implementation of `assert`, all assertions run in, what Node.js calls, the strict assertion mode. In strict assertion mode, non-strict methods behave like their corresponding strict methods. For example, `deepEqual()` will behave like `deepStrictEqual()`. | ||
| In the Workers implementation of `assert`, all assertions run in, what Node.js calls, the strict assertion mode. In strict assertion mode, non-strict methods behave like their corresponding strict methods. For example, `deepEqual()` will behave like `deepStrictEqual()`. | ||
| ::: | ||
|
|
||
| Refer to the [Node.js documentation for `assert`](https://nodejs.org/dist/latest-v19.x/docs/api/assert.html) for more information. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| --- | ||
| pcx_content_type: configuration | ||
| title: dns | ||
| --- | ||
|
|
||
| import { Render, TypeScriptExample } from "~/components"; | ||
|
|
||
| <Render file="nodejs-compat-howto" /> | ||
|
|
||
| You can use [`node:dns`](https://nodejs.org/api/dns.html) for name resolution via [DNS over HTTPS](/1.1.1.1/encryption/dns-over-https/) using | ||
| [Cloudflare DNS](https://www.cloudflare.com/application-services/products/dns/) at 1.1.1.1. | ||
|
|
||
| <TypeScriptExample filename="index.ts"> | ||
| ```ts | ||
| import dns from "node:dns"; | ||
|
|
||
| dns.lookup("example.org", (_err: any, address: string, ipFamily: number) => | ||
| console.log(`address: ${address} family: IPv${ipFamily}`)); | ||
| ``` | ||
| </TypeScriptExample> | ||
|
|
||
| :::note | ||
|
|
||
| DNS requests will execute a subrequest, counts for your [Worker's subrequest limit](/workers/platform/limits/#subrequests). | ||
|
|
||
| ::: | ||
|
|
||
| The full `node:dns` API is documented in the [Node.js documentation for `node:dns`](https://nodejs.org/api/dns.html). | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| --- | ||
| pcx_content_type: configuration | ||
| title: net | ||
| --- | ||
|
|
||
| import { Render, TypeScriptExample } from "~/components"; | ||
|
|
||
| <Render file="nodejs-compat-howto" /> | ||
|
|
||
| You can use [`node:net`](https://nodejs.org/api/net.html) to create a direct connection to servers via a TCP sockets | ||
| with [`net.Socket`](https://nodejs.org/api/net.html#class-netsocket). | ||
|
|
||
| This makes it possible to connect to databases such as [MySQL](https://www.mysql.com/), [PostgreSQL](https://www.postgresql.org/), | ||
| [Redis](https://redis.io/), or [MongoDB](https://github.com/mongodb/mongo). Though for production use, we recommend that you connect | ||
| using TLS, which can be done with the [`cloudflare:sockets`](/workers/runtime-apis/tcp-sockets/#connect) | ||
| module, or prefereably by using [Hyperdrive](/hyperdrive), which includes | ||
| [connection pooling](/hyperdrive/configuration/how-hyperdrive-works/#connection-pooling) | ||
| and [query caching](/hyperdrive/configuration/how-hyperdrive-works/#query-caching). | ||
|
|
||
| These functions use [`connect`](/workers/runtime-apis/tcp-sockets/#connect) functionality from the built-in `cloudflare:sockets` module. | ||
|
|
||
| <TypeScriptExample filename="index.ts"> | ||
| ```ts | ||
| import net from "node:net"; | ||
|
|
||
| const exampleIP = "127.0.0.1"; | ||
|
|
||
| export default { | ||
| async fetch(req): Promise<Response> { | ||
| const socket = new net.Socket(); | ||
| socket.connect(4000, exampleIP, function () { | ||
| console.log("Connected"); | ||
| }); | ||
|
|
||
| socket.write("Hello, Server!"); | ||
| socket.end(); | ||
|
|
||
| return new Response("Wrote to server", { status: 200 }); | ||
| }, | ||
| } satisfies ExportedHandler; | ||
|
|
||
| ``` | ||
| </TypeScriptExample> | ||
|
|
||
| Additionally, other APIs such as [`net.BlockList`](https://nodejs.org/api/net.html#class-netblocklist) | ||
| and [`net.SocketAddress`](https://nodejs.org/api/net.html#class-netsocketaddress) are available. | ||
|
|
||
| Note that the [`net.Server`](https://nodejs.org/api/net.html#class-netserver) class is not supported by Workers. | ||
|
|
||
| The full `node:net` API is documented in the [Node.js documentation for `node:net`](https://nodejs.org/api/net.html). |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,24 +1,18 @@ | ||
| --- | ||
| pcx_content_type: configuration | ||
| title: path | ||
|
|
||
| --- | ||
|
|
||
| import { Render } from "~/components" | ||
| import { Render } from "~/components"; | ||
|
|
||
| <Render file="nodejs-compat-howto" /> | ||
|
|
||
| The [`node:path`](https://nodejs.org/api/path.html) module provides utilities for working with file and directory paths. The `node:path` module can be accessed using: | ||
|
|
||
| ```js | ||
| import path from "node:path" | ||
| path.join('/foo', 'bar', 'baz/asdf', 'quux', '..'); | ||
| import path from "node:path"; | ||
| path.join("/foo", "bar", "baz/asdf", "quux", ".."); | ||
| // Returns: '/foo/bar/baz/asdf' | ||
| ``` | ||
|
|
||
| :::note | ||
|
|
||
| In the Workers implementation of `path`, the [path.win32](https://nodejs.org/api/path.html#windows-vs-posix) variants of the path API are not implemented, and will throw an exception. | ||
| ::: | ||
|
|
||
| Refer to the [Node.js documentation for `path`](https://nodejs.org/api/path.html) for more information. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| --- | ||
| pcx_content_type: configuration | ||
| title: timers | ||
| --- | ||
|
|
||
| import { Render, TypeScriptExample } from "~/components"; | ||
|
|
||
| <Render file="nodejs-compat-howto" /> | ||
|
|
||
| Use [`node:timers`](https://nodejs.org/api/timers.html) APIs to schedule functions to be executed later. | ||
|
|
||
| This includes [`setTimeout`](https://nodejs.org/api/timers.html#settimeoutcallback-delay-args) for calling a function after a delay, | ||
| [`setInterval`](https://nodejs.org/api/timers.html#clearintervaltimeout) for calling a function repeatedly, | ||
| and [`setImmediate`](https://nodejs.org/api/timers.html#setimmediatecallback-args) for calling a function in the next iteration of the event loop. | ||
|
|
||
| <TypeScriptExample filename="index.ts"> | ||
| ```ts | ||
| import timers from "node:timers"; | ||
|
|
||
| console.log("first"); | ||
| timers.setTimeout(() => { | ||
| console.log("last"); | ||
| }, 10); | ||
|
|
||
| timers.setTimeout(() => { | ||
| console.log("next"); | ||
| }); | ||
| ``` | ||
| </TypeScriptExample> | ||
|
|
||
|
Contributor
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 think we should callout that the We could also say "not fully interoperable" but we would then need to give details on what is vs what is not which could bring confusion. For example (not sure we want to add this here): import timers from 'node:timer';
// no-op, the timer is not cancelled
clearTimeout(timers.setTimeout(...));
// the timer is cancelled
timers.clearTimeout(setTimeout(...));
Member
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.
IMHO: This should be mentioned in nodejs docs about node:timers, not in cloudflare.
Contributor
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. Well that is the exact issue here, this problem is specific to Cloudflare and does not exist on Node
Contributor
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. That's why I think we should add a callout - we are not compatible with Node.js here.
Contributor
Author
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. Do we have a thread on this generally? I'm not sure I fully understand what's going on here. On the example provided, so in a Node.js environment the timer could be cancelled in the first example If so, yeah I think that deserves a callout if behavior differs from Node.js. Any other cases to mention?
Contributor
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.
Your understanding is correct.
Member
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. setTimeout, setImmediate and setInterval doesn't return Timeout/Immediate classes on cloudflare because the web spec says that it should return an ID. Whereas, Node.js doesn't implement this. But this fact doesn't change with the new changes. We are not changing the existing behavior, we are just adding new modules. I don't think this changelog is the place to list every single differences that we have. I recommend keeping this post as simple as possible, and just mention the behavior that we have added. IMHO: Other information should be mentioned in the documentation (not on changelog)
Contributor
Author
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. Okay, I updated the node:timers docs to include a Note here. I didn't go super in depth though FWIW. Kept out of the changelog since this changelog post is already super long and felt docs-y.
Contributor
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. Oh... You probably got confused by the title of the PR (can it be updated?)
Contributor
Author
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. Oh, no I gotcha - Just being overly clear on where I added it :) |
||
| :::note | ||
| Due to [security-based restrictions on timers](/workers/reference/security-model/#step-1-disallow-timers-and-multi-threading) in Workers, | ||
| timers are limited to returning the time of the last I/O. This means that while setTimeout, setInterval, and setImmediate will defer your function execution | ||
| until after other events have run, they will not delay them for the full time specified. | ||
| ::: | ||
|
|
||
| :::note | ||
| When called from a global level (on [`globalThis`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis)), | ||
| functions such as `clearTimeout` and `setTimeout` will respect web standards rather than Node.js-specific functionality. For complete Node.js | ||
| compatibility, you must call functions from the `node:timers` module. | ||
| ::: | ||
|
|
||
| The full `node:timers` API is documented in the [Node.js documentation for `node:timers`](https://nodejs.org/api/timers.html). | ||
Uh oh!
There was an error while loading. Please reload this page.