Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>
20 changes: 8 additions & 12 deletions src/content/docs/workers/runtime-apis/nodejs/assert.mdx
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.
28 changes: 28 additions & 0 deletions src/content/docs/workers/runtime-apis/nodejs/dns.mdx
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).
50 changes: 50 additions & 0 deletions src/content/docs/workers/runtime-apis/nodejs/net.mdx
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).
12 changes: 3 additions & 9 deletions src/content/docs/workers/runtime-apis/nodejs/path.mdx
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.
43 changes: 43 additions & 0 deletions src/content/docs/workers/runtime-apis/nodejs/timers.mdx
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>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should callout that the node:timers are "not interoperable" with the global timer functions because this behavior is different from Node.

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(...));

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should callout that the node:timers are "not interoperable" with the global timer functions because this behavior is different from Node.

IMHO: This should be mentioned in nodejs docs about node:timers, not in cloudflare.

Copy link
Contributor

@vicb vicb Jan 27, 2025

Choose a reason for hiding this comment

The 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

Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 clearTimeout(timers.setTimeout(...)); but for us it is not cancelled?

If so, yeah I think that deserves a callout if behavior differs from Node.js.

Any other cases to mention?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the example provided, so in a Node.js environment the timer could be cancelled in the first example clearTimeout(timers.setTimeout(...)); but for us it is not cancelled?

Your understanding is correct.

Copy link
Member

Choose a reason for hiding this comment

The 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)

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

Copy link
Contributor

Choose a reason for hiding this comment

The 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?)
But we are now commenting on the documentation for the timers module, similar to https://developers.cloudflare.com/workers/runtime-apis/nodejs/process/

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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).
Loading