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 theregisterNodeJsCompatModulesfunction, theisNodeJsCompatEnabledhelper, and theNODEJS_MODULESmacro that lists all available C++ modules.src/workerd/io/compatibility-date.capnp— Defines the Cap'n Proto schema fornodeJsCompat,nodeJsCompatV2, and per-module flags likeenableNodeJsFsModule.src/workerd/api/modules.h— CallsregisterNodeJsCompatModulesduring 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 publicnode:entry points and are compiled into theNODE_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 asfs-nodejs-test.wd-test) that validate the compatibility layer behavior.
Summary
- The Node.js compatibility layer is a flag-gated system in
workerdthat exposes Node.js built-in modules through internal C++ implementations and TypeScript shims. - Activation requires the
nodejs_compatornodejs_compat_v2flag in the Workerd configuration, triggering theisNodeJsCompatEnabledcheck insrc/workerd/api/node/node.h. - The
registerNodeJsCompatModulesfunction registers C++ modules and filters theNODE_BUNDLEat runtime based on per-module feature flags. - Modules are implemented in
src/workerd/api/node/*.c++and exposed vianode:imports through the TypeScript shims insrc/node/*.ts. - Granular control allows enabling specific modules (like
fsorhttp) 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →