What Is the Node.js Compatibility Layer in Workerd?

The Node.js compatibility layer in workerd is a flag-gated collection of internal C++ modules that exposes Node.js built-in APIs—such as fs, http, and crypto—to Workers, enabling standard node: prefixed imports when the nodejs_compat compatibility flag is activated.

The Node.js compatibility layer allows Cloudflare's workerd runtime to execute code written for Node.js by providing sandboxed implementations of native Node modules. This system bridges the gap between Node.js APIs and workerd's secure, isolate-based architecture, letting developers import modules like node:fs and node:http directly inside their Workers.

Architecture of the Compatibility Layer

The layer consists of two coordinated components: C++ native implementations that provide the actual functionality, and TypeScript shims that expose these implementations through standard node: import specifiers. The system is controlled through compatibility flags defined in the runtime's configuration schema.

Compatibility Flags and Feature Toggles

The availability of the Node.js compatibility layer is determined by the nodeJsCompat and nodeJsCompatV2 flags declared in src/workerd/io/compatibility-date.capnp. When a Worker starts, the runtime evaluates these flags to decide which modules to expose.

In src/workerd/api/node/node.h, the helper function isNodeJsCompatEnabled checks these flags at runtime:

bool isNodeJsCompatEnabled(auto featureFlags) {
  return featureFlags.getNodeJsCompat() || featureFlags.getNodeJsCompatV2();
}

If this check returns false, only minimal internal modules (such as those required for console output) remain available. If true, the system proceeds to evaluate per-module flags like enableNodeJsFsModule and enableNodejsHttpModules to determine exactly which APIs to surface.

Module Registration Flow

The central registration logic resides in registerNodeJsCompatModules, defined in src/workerd/api/node/node.h. This function wires the C++ implementations into the JavaScript runtime through the JSG (JavaScript Glue) module system:

template <class Registry>
void registerNodeJsCompatModules(Registry& registry, auto featureFlags) {
  // Register C++ modules as built-ins
  NODEJS_MODULES(V);  // Macro expands to addBuiltinModule<T>(name, INTERNAL)
  
  if (featureFlags.getWorkerdExperimental()) {
    NODEJS_MODULES_EXPERIMENTAL(V);
  }
  
  // Filter the JavaScript bundle based on flags
  bool nodeJsCompatEnabled = isNodeJsCompatEnabled(featureFlags);
  registry.addBuiltinBundleFiltered(NODE_BUNDLE, [&](jsg::Module::Reader module) {
    if (!nodeJsCompatEnabled) return module.getType() == jsg::ModuleType::INTERNAL;
    if (isNodeHttpModule(module.getName())) return featureFlags.getEnableNodejsHttpModules();
    if (module.getName() == "node:fs"_kj) return featureFlags.getEnableNodeJsFsModule();
    return true;
  });
}

This function performs two critical tasks. First, it registers all internal C++ modules (found in src/workerd/api/node/*.c++) as built-in modules available to the JavaScript engine. Second, it filters the NODE_BUNDLE—a compiled JavaScript bundle generated from the TypeScript sources in src/node/*.ts—based on the current feature flags.

The TypeScript Shim Layer

The public API surface is provided by TypeScript files under src/node/ that re-export the underlying C++ functionality. These shims use standard node: import specifiers (e.g., node:fs, node:http) that mirror Node.js's own module resolution. When bundled into NODE_BUNDLE, these files create the mapping between JavaScript import statements and the native C++ implementations.

Enabling Node.js APIs in Your Worker

To use the Node.js compatibility layer in workerd, you must enable the appropriate compatibility flag in your Workerd configuration and import modules using the node: prefix.

Configuration Example

Activate the layer in your .capnp configuration file by including the nodejs_compat flag:

const unitTests :Workerd.Config = (
  services = [(
    name = "my-worker",
    worker = (
      modules = [(name = "worker", esModule = embed "my-worker.js")],
      compatibilityFlags = ["nodejs_compat"],
    ),
  )],
);

The compatibilityFlags array accepts nodejs_compat or nodejs_compat_v2, both of which trigger the isNodeJsCompatEnabled check in the runtime.

Importing Node.js Modules

Once enabled, your Worker can import Node.js built-ins exactly as you would in a standard Node.js environment:

// my-worker.js
import { readFile } from "node:fs";
import http from "node:http";

readFile("example.txt", "utf8", (err, data) => {
  if (err) throw err;
  console.log("File contents:", data);
});

http.createServer((req, res) => {
  res.writeHead(200);
  res.end("Hello from Node-compatible Workerd!");
}).listen(8080);

These imports resolve to the C++ implementations in src/workerd/api/node/fs.c++ and src/workerd/api/node/http.c++, providing native-like performance while maintaining workerd's security sandbox.

Granular Module Control

You can expose specific modules without enabling the entire compatibility layer using per-module feature flags. For example, to enable only the file system module while disabling HTTP:

compatibilityFlags = [
  "nodejs_compat",
  "enable_nodejs_fs_module",
  "no_enable_nodejs_http_modules"
];

The registerNodeJsCompatModules function checks these flags individually when filtering the NODE_BUNDLE, allowing precise control over which Node.js APIs are available to your Worker.

Key Implementation Files

Understanding the Node.js compatibility layer requires familiarity with these specific source files in the cloudflare/workerd repository:

  • src/workerd/api/node/node.h — Contains the registerNodeJsCompatModules function, the isNodeJsCompatEnabled helper, and the NODEJS_MODULES macro that lists all available C++ modules.
  • src/workerd/io/compatibility-date.capnp — Defines the Cap'n Proto schema for nodeJsCompat, nodeJsCompatV2, and per-module flags like enableNodeJsFsModule.
  • src/workerd/api/modules.h — Calls registerNodeJsCompatModules during the global JSG module registry construction.
  • src/workerd/api/node/*.c++ — C++ implementations of individual Node.js modules (e.g., fs.c++, http.c++, crypto.c++).
  • src/node/*.ts — TypeScript façade files that provide the public node: entry points and are compiled into the NODE_BUNDLE.
  • src/node/README.md — High-level documentation describing the shim layer architecture.
  • src/workerd/api/node/tests/*-nodejs-test.wd-test — End-to-end test files (such as fs-nodejs-test.wd-test) that validate the compatibility layer behavior.

Summary

  • The Node.js compatibility layer is a flag-gated system in workerd that exposes Node.js built-in modules through internal C++ implementations and TypeScript shims.
  • Activation requires the nodejs_compat or nodejs_compat_v2 flag in the Workerd configuration, triggering the isNodeJsCompatEnabled check in src/workerd/api/node/node.h.
  • The registerNodeJsCompatModules function registers C++ modules and filters the NODE_BUNDLE at runtime based on per-module feature flags.
  • Modules are implemented in src/workerd/api/node/*.c++ and exposed via node: imports through the TypeScript shims in src/node/*.ts.
  • Granular control allows enabling specific modules (like fs or http) individually without activating the entire compatibility suite.

Frequently Asked Questions

What is the difference between nodejs_compat and nodejs_compat_v2?

Both flags enable the Node.js compatibility layer in workerd by triggering the isNodeJsCompatEnabled function in src/workerd/api/node/node.h, which returns true if either flag is set. The specific behavioral differences between v1 and v2 are defined in the compatibility-date schema at src/workerd/io/compatibility-date.capnp, with v2 typically representing an updated or expanded set of supported APIs and behaviors.

Can I use all Node.js modules in workerd?

No, workerd implements a subset of Node.js built-in modules based on security constraints and runtime capabilities. The available modules are listed in the NODEJS_MODULES macro in src/workerd/api/node/node.h and include common APIs like fs, http, crypto, and process, but exclude others that conflict with the sandboxed Worker environment. Per-module flags like enableNodeJsFsModule allow granular control over which specific modules are exposed.

How does workerd handle Node.js module security?

The Node.js compatibility layer maintains workerd's security model by implementing Node.js APIs as internal C++ modules that operate within the existing sandbox restrictions. File system operations in node:fs, for example, are subject to the same read-only or restricted access constraints as standard workerd storage APIs. The registerNodeJsCompatModules function ensures that only explicitly enabled modules are exposed, preventing unauthorized access to potentially dangerous native functionality.

Where are the actual Node.js API implementations located?

The concrete implementations reside in src/workerd/api/node/*.c++ files (such as fs.c++, http.c++, and crypto.c++). These C++ files provide the native functionality that backs the JavaScript-facing APIs. The public node: import specifiers are defined in src/node/*.ts as TypeScript shims that forward calls to these C++ implementations, creating the bridge between standard Node.js code patterns and workerd's native runtime.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →