Deno provides a compatibility layer for Node.js built-in modules, allowing you to run Node.js code with minimal changes. This enables gradual migration and use of the Node.js ecosystem.
Built-in Node Modules
Deno supports most Node.js built-in modules through the node: specifier:
import * as fs from "node:fs" ;
import * as path from "node:path" ;
import * as http from "node:http" ;
import { Buffer } from "node:buffer" ;
import process from "node:process" ;
Supported Node Modules
File System
node:fs
node:fs/promises
node:path
Networking
node:http
node:https
node:net
node:dgram
node:dns
Utilities
node:buffer
node:events
node:stream
node:util
node:url
Process
node:process
node:child_process
node:os
Other
node:assert
node:querystring
node:timers
node:zlib
Usage Examples
File System Operations
import * as fs from "node:fs/promises" ;
import * as path from "node:path" ;
// Read file
const data = await fs . readFile ( "file.txt" , "utf-8" );
// Write file
await fs . writeFile ( "output.txt" , "Hello, World!" );
// Path operations
const fullPath = path . join ( "/home" , "user" , "file.txt" );
const ext = path . extname ( "file.txt" ); // ".txt"
HTTP Server
import http from "node:http" ;
const server = http . createServer (( req , res ) => {
res . writeHead ( 200 , { "Content-Type" : "text/plain" });
res . end ( "Hello from Deno with Node.js compatibility! \n " );
});
server . listen ( 3000 , () => {
console . log ( "Server running at http://localhost:3000/" );
});
Buffer Operations
import { Buffer } from "node:buffer" ;
const buf = Buffer . from ( "Hello, World!" , "utf-8" );
console . log ( buf . toString ( "hex" ));
const buf2 = Buffer . alloc ( 10 );
buf2 . write ( "Deno" );
Process and Environment
import process from "node:process" ;
console . log ( "Node version:" , process . version );
console . log ( "Platform:" , process . platform );
console . log ( "Environment:" , process . env . HOME );
process . on ( "exit" , ( code ) => {
console . log ( `Process exiting with code ${ code } ` );
});
CommonJS Support
Deno automatically handles CommonJS modules from npm packages:
// Works with CJS packages
import express from "npm:express" ;
import _ from "npm:lodash" ;
// Also works with require() in .cjs files
const fs = require ( "node:fs" );
Enabling CJS Detection
For better CommonJS support, enable the unstable feature:
{
"unstable" : [ "detect-cjs" ]
}
Or via CLI:
deno run --unstable-detect-cjs script.js
Global Node Variables
Deno provides Node.js globals when needed:
// Available globals
console . log ( process . version );
console . log ( __dirname );
console . log ( __filename );
console . log ( global );
Enable explicit Node globals:
{
"unstable" : [ "node-globals" ]
}
Package.json Support
Deno can read package.json for Node.js compatibility:
{
"name" : "my-app" ,
"type" : "module" ,
"dependencies" : {
"express" : "^4.18.2"
},
"scripts" : {
"start" : "node index.js"
}
}
Deno will:
Resolve dependencies from package.json
Respect the "type": "module" field
Use node_modules if configured
node_modules Compatibility
For maximum Node.js compatibility, enable node_modules:
{
"nodeModulesDir" : "auto"
}
This creates a local node_modules directory compatible with Node.js tools.
Bring Your Own node_modules (BYONM)
Use existing node_modules from npm/yarn/pnpm:
{
"unstable" : [ "byonm" ],
"nodeModulesDir" : "manual"
}
Then use your preferred package manager:
npm install
deno run main.ts
Bare Specifiers
Enable Node.js-style bare specifiers for built-in modules:
{
"unstable" : [ "bare-node-builtins" ]
}
Now you can import without the node: prefix:
// With bare-node-builtins enabled
import fs from "fs" ;
import path from "path" ;
import http from "http" ;
Explicit node: prefixes are recommended for clarity and future compatibility.
Limitations
While Deno provides extensive Node.js compatibility, some features are not supported:
node:vm - Virtual machine contexts
node:v8 - V8 engine internals
node:domain - Deprecated domain module
Some native addons requiring N-API
node:child_process - Some options may differ
node:cluster - Limited support
node:worker_threads - Use Deno’s Web Workers instead
Migration from Node.js
Migrating a Node.js project to Deno:
Add node: prefixes
Update built-in imports: // Before (Node.js)
import fs from "fs" ;
// After (Deno)
import fs from "node:fs" ;
Convert to ES modules
Replace CommonJS with ES modules: // Before
const express = require ( "express" );
module . exports = app ;
// After
import express from "npm:express" ;
export default app ;
Update package imports
Add npm: prefix or use import map: {
"imports" : {
"express" : "npm:express@^4.18.2"
}
}
Configure compatibility
{
"nodeModulesDir" : "auto" ,
"unstable" : [ "detect-cjs" , "bare-node-builtins" ]
}
Update scripts
Replace npm scripts with Deno tasks: {
"tasks" : {
"start" : "deno run --allow-net --allow-read main.ts" ,
"dev" : "deno run --allow-net --allow-read --watch main.ts"
}
}
Real-World Example: Express Server Migration
Node.js Version
const express = require ( "express" );
const fs = require ( "fs" );
const path = require ( "path" );
const app = express ();
app . get ( "/" , ( req , res ) => {
const data = fs . readFileSync ( path . join ( __dirname , "data.txt" ), "utf-8" );
res . send ( data );
});
app . listen ( 3000 );
Deno Version
import express from "npm:express@^4.18.2" ;
import { readFileSync } from "node:fs" ;
import { join , dirname } from "node:path" ;
import { fileURLToPath } from "node:url" ;
const __dirname = dirname ( fileURLToPath ( import . meta . url ));
const app = express ();
app . get ( "/" , ( req , res ) => {
const data = readFileSync ( join ( __dirname , "data.txt" ), "utf-8" );
res . send ( data );
});
app . listen ( 3000 );
{
"tasks" : {
"start" : "deno run --allow-net --allow-read main.ts"
},
"imports" : {
"express" : "npm:express@^4.18.2"
}
}
Deno-First Alternatives
When possible, use Deno’s native APIs instead of Node.js compatibility:
Node.js Compatibility
Deno Native
import { readFile } from "node:fs/promises" ;
import http from "node:http" ;
const data = await readFile ( "file.txt" , "utf-8" );
const server = http . createServer (( req , res ) => {
res . end ( "Hello" );
});
Best Practices
Use node: prefix Always use node: prefix for clarity: import fs from "node:fs"
Prefer Deno APIs Use Deno’s native APIs when possible for better performance
Test thoroughly Test Node.js compatibility features before production
Gradual migration Migrate incrementally, using compatibility layer as needed
Checking Compatibility
Test if a Node.js module works in Deno:
# Try importing
deno eval "import 'node:fs'; console.log('Works!')"
# Check with npm package
deno eval "import express from 'npm:express'; console.log('Express works!')"