Runtime environment constraints
workerd operates in a different environment than Node.js, which affects API availability and behavior.No filesystem access
workerd does not have direct access to a traditional filesystem. Thenode:fs module is partially implemented but with significant limitations:
- File operations are restricted or non-functional
- Paths do not correspond to a real filesystem
- Many filesystem APIs will throw errors or return stub values
No child processes
Thenode:child_process module is a non-functional stub. You cannot spawn child processes or execute shell commands in workerd.
No threads
Thenode:worker_threads module is a non-functional stub. workerd uses isolates for concurrency instead of threads.
For concurrent execution, use:
- Multiple Workers with service bindings
- Durable Objects for stateful concurrent operations
- Standard Web Workers API (where available)
Limited networking
Network modules likenode:net, node:dgram, and node:tls have limited functionality:
- TCP and UDP sockets are restricted
- TLS is available primarily through the
fetchAPI and Web Crypto - Direct socket access may not work as in Node.js
fetch() API for HTTP/HTTPS requests instead of node:http or node:https.
Process and environment
process.env behavior
Theprocess.env object behaves differently in workerd:
- Requires the
nodejs_compat_populate_process_envflag to be populated - Environment variables come from worker bindings, not the system environment
- Some environment variables common in Node.js (like
NODE_ENV) may not be set
process properties
Manyprocess properties are stubs or provide workerd-specific values:
process.platformreturns a fixed value (not the actual host OS)process.archreturns a fixed valueprocess.versionreflects workerd’s Node.js compatibility version, not Node.js itselfprocess.pid,process.ppidmay return stub valuesprocess.cwd(),process.chdir()are limited or non-functional
No process signals
Signal handling APIs likeprocess.on('SIGTERM') are non-functional. Worker lifecycle is managed by workerd, not through signals.
Module system differences
Module resolution
workerd uses its own module system with some differences from Node.js:- Module resolution follows workerd’s rules, not Node.js’s
node_modulesalgorithm - Some Node.js-specific module resolution features may not work
- The
node:prefix is required for Node.js built-ins unlessnodejs_compat_v2is enabled
Conditional imports
Packageexports conditions may behave differently:
- workerd looks for
workerd,worker, andbrowserconditions - The
nodecondition is not typically matched - This can affect which code paths in packages are used
Global objects
Global differences
workerd prioritizes Web Platform Standard globals over Node.js globals:require() and __dirname
CommonJS-specific globals are not available:require()is not available in ES modules (useimportinstead)__dirnameand__filenameare not definedmodule.exportsis not available (useexportinstead)
Crypto differences
Web Crypto takes precedence
workerd implements the Web Crypto API (crypto.subtle) as the primary crypto interface. The node:crypto module is built on top of the same underlying implementation (BoringSSL) but may have subtle differences:
- Algorithm names may differ between Web Crypto and Node.js crypto
- Key formats and import/export behavior may vary
- Some Node.js-specific crypto APIs may be missing
Streams differences
workerd provides both Node.js streams and Web Streams:Web Streams are preferred
The standardReadableStream, WritableStream, and TransformStream from the Streams API are the primary stream interfaces. Node.js streams are provided for compatibility but may have subtle differences:
- Performance characteristics may differ
- Backpressure handling may work differently
- Some advanced Node.js stream features may be missing
Stream interoperability
Conversion between Node.js streams and Web Streams is supported:Module-specific differences
Buffer implementation
TheBuffer class is fully implemented but built on Web Platform standards:
- Backed by
Uint8ArrayandArrayBuffer - Performance characteristics may differ from Node.js
- All standard
Buffermethods are available
Events implementation
TheEventEmitter class is provided but some advanced features may differ:
- Event loop timing may behave differently
- Error handling in event listeners may have subtle differences
Timers behavior
timer functions (setTimeout, setInterval) are implemented but subject to workerd’s execution model:
- Timers do not prevent the isolate from being terminated
- Timer precision may differ from Node.js
- Very long timers may not work as expected
URL parsing
workerd uses the WHATWG URL standard via theada-url library:
- Fully spec-compliant URL parsing
- Legacy Node.js URL APIs (
url.parse(),url.format()) are available but may behave differently - Use the
URLclass for new code
Testing and debugging
Limited debugging tools
Node.js debugging tools do not work with workerd:node:inspectoris a non-functional stub- Chrome DevTools cannot attach to workerd isolates directly
console.log()is your primary debugging tool
Test runner differences
Thenode:test module is a stub. Use alternative testing frameworks:
- Run tests in workerd using the
workerdbinary with test configurations - Use external test frameworks that support Workers
- Write
.wd-testfiles for workerd-specific testing
Performance considerations
Cold start optimization
workerd optimizes for fast cold starts, which affects implementation:- Some APIs may be lazily initialized
- Large dependencies in Node.js modules may impact startup time
- Minimize use of heavy Node.js APIs in global scope
Memory constraints
Workers run in isolates with memory limits:- Large buffers or data structures may hit memory limits
- Node.js APIs that allocate significant memory may fail
- Monitor memory usage with worker metrics
Compatibility flag requirements
Many Node.js modules require explicit compatibility flags:| Module | Flag |
|---|---|
node:http, node:https | enable_nodejs_http_modules |
node:_http_server | enable_nodejs_http_server_modules |
node:fs | enable_nodejs_fs_module |
node:os | enable_nodejs_os_module |
node:http2 | enable_nodejs_http2_module |
node:console | enable_nodejs_console_module |
node:vm | enable_nodejs_vm_module |
node:perf_hooks | enable_nodejs_perf_hooks_module |
node:sqlite | enable_nodejs_sqlite_module |
Best practices
To maximize compatibility and avoid issues:Prefer Web Platform APIs
Use standard Web Platform APIs like
fetch(), Web Crypto, and Web Streams instead of Node.js equivalents when possible.Test in workerd
Always test your code in workerd, not just Node.js. Behavior may differ in subtle ways.
Avoid filesystem and process APIs
Do not rely on
node:fs, node:child_process, or process management APIs. They will not work as expected.Use appropriate storage
Instead of filesystem operations, use:
- KV for key-value storage
- R2 for object storage
- Durable Objects for stateful operations
- D1 for SQL databases
Check compatibility flags
Ensure you have the necessary compatibility flags enabled for the Node.js modules you use.
Migration from Node.js
When migrating Node.js applications to workerd:- Identify Node.js API usage: Audit your code for Node.js built-in module imports
- Check supported APIs: Verify that the modules you need are supported (see supported APIs)
- Replace unsupported APIs: Find alternatives for unsupported or limited APIs
- Enable compatibility flags: Add necessary flags to your configuration
- Test thoroughly: Run your application in workerd and test all code paths
- Monitor for errors: Watch for runtime errors related to missing or limited APIs
Getting help
If you encounter issues with Node.js compatibility:- Check the supported APIs page to verify the API is implemented
- Review this differences page for known limitations
- Test your code in workerd to reproduce the issue
- Report issues to the workerd project with minimal reproduction cases