Skip to main content
On this page

Node compatibility and interop

Modern Node.js projects will run in Deno with little to no reworking required. However, there are some key differences between the two runtimes that you can take advantage of to make your code simpler and smaller when migrating your Node.js projects to Deno.

Migrating from Node.js to Deno Jump to heading

Running your Node.js project with Deno is a straightforward process, the main points to be aware of are:

  1. Usage of Node.js globals (like process, Buffer, etc)
  2. Imported Node.js built-in modules need the node: specifier (fs -> node:fs)

Tip

If your project is written with CommonJS (i.e. require), you will need to update it to use ECMAScript modules, check out our helpful CommonJS to ESM guide to get you up and running with Deno.

Node.js built-ins Jump to heading

In Node.js 20 and earlier, built-in modules in the Node.js standard library could be imported with "bare specifiers". Consider the Node program below with a .mjs extension:

index.mjs
import * as os from "os";
console.log(os.cpus());

The os module is built in to the Node.js runtime, and can be imported using a bare specifier as above.

.mjs extensions not required in Deno

The .mjs file extension is supported but not required in Deno. Because Node doesn't support ESM by default, it requires you to name any files that use ESM with a .mjs file extension.

Node specifiers Jump to heading

Deno provides a compatibility layer that allows the use of Node.js built-in APIs within Deno programs. However, in order to use them, you will need to add the node: specifier to any import statements that use them.

For example - if you update the code above to be this instead:

import * as os from "node:os";
console.log(os.cpus());

And run it with deno run index.mjs - you will notice you get the same output as running the program in Node.js. Updating any imports in your application to use node: specifiers should enable any code using Node built-ins to function as it did in Node.js.

Runtime permissions in Deno Jump to heading

Consider the following simple Express server:

import express from "npm:express@4";

const app = express();

app.get("/", function (_req, res) {
  res.send("hello");
});

app.listen(3000, () => {
  console.log("Express listening on :3000");
});

If you run the above with deno run server.js, you will be prompted for permissions required to execute the code and its dependencies. For example:

$ deno run server.js
┌ ⚠️  Deno requests net access to "0.0.0.0:8000".
├ Requested by `Deno.listen()` API.
├ Run again with --allow-net to bypass this prompt.
└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all net permissions) >

Deno features runtime security by default, meaning that you as the developer must opt in to giving your code access to the filesystem, network, system environment, and more. Doing this prevents supply chain attacks and other potential vulnerabilities in your code. By comparison, Node.js has no concept of runtime security, with all code executed with the same level of permission as the user running the code.

To run your code as you would in Node.js, you can pass the -A flag to enable all permissions.

deno run -A server.js

For more granular control, you can enable access to specific features by opting in to individual permissions.

Running scripts from package.json Jump to heading

Deno supports running npm scripts natively with the deno task subcommand. Consider the following Node.js project with a script called start inside its package.json:

package.json
{
  "name": "my-project",
  "scripts": {
    "start": "eslint"
  }
}

You can execute this script with Deno by running:

deno task start

Node.js global objects Jump to heading

In Node.js, there are a number of global objects available in the scope of all programs, like the process object, Buffer, or __dirname and __filename.

Deno does not add additional objects and variables to the global scope, other than the Deno global. Any API that doesn't exist as a web-standard browser API will be found in Deno. Alternatively, you can import Node.js built-in modules using the node: specifier.

import process from "node:process";
import { Buffer } from "node:buffer";

const __filename = import.meta.filename;
const __dirname = import.meta.dirname;

Note

If you do run into a problem with Node.js compatibility, please let us know by opening an issue on GitHub.

Optional improvements with Deno's built-in tools Jump to heading

One of Deno's core strengths is a unified toolchain that comes with support for TypeScript out of the box, and tools like a linter, formatter and a test runner. Switching to Deno allows you to simplify your toolchain and reduces the number of moving components in your project. Deno also has a more secure runtime, with runtime permissions that allow you to control what your code can access.

deno.json (optional) Jump to heading

Deno has its own config file, deno.json or deno.jsonc, which can be used to configure your project. You can use it to define tasks, dependencies, path mappings, and other runtime configurations.

Migrating npm scripts to deno.json (optional) Jump to heading

If preferred, you can move your npm scripts over to deno.json, where they can be run using deno task. This allows you to manage all necessary permission flags and other runtime configuration in one place.

{
  "tasks": {
    "dev": "deno run --allow-net --allow-read --allow-env server.js"
  }
}
deno task dev

Migrating npm dependencies to deno.json (optional) Jump to heading

You can also migrate your dependencies over to deno.json. Deno supports importing dependencies from external package repositories, local files, and/or URLs. To import your npm dependencies, you can add them to the imports field in deno.json, and add the npm: specifier to the import path:

{
  "imports": {
    "express": "npm:express@4"
  }
}

Deno supports multiple package registries and allows you to import dependencies from npm, JSR and HTTP URLs.

{
  "imports": {
    "express": "npm:express@4",
    "@luca/cases": "jsr:@luca/cases@1",
    "foo": "https://example.com/foo.ts"
  }
}

Linting (optional) Jump to heading

Deno ships with a built-in linter that is written with performance in mind. Deno can lint large projects in just a few milliseconds. You can try it out on your project by running:

deno lint

This will lint all files in your project. When the linter detects a problem, it will show the line in your editor and in the terminal output. An example of what that might look like:

error[no-constant-condition]: Use of a constant expressions as conditions is not allowed.
 --> /my-project/bar.ts:1:5
  | 
1 | if (true) {
  |     ^^^^
  = hint: Remove the constant expression

  docs: https://lint.deno.land/rules/no-constant-condition


Found 1 problem
Checked 4 files

Many linting issues can be fixed automatically by passing the --fix flag:

deno lint --fix

A full list of all supported linting rules can be found on https://lint.deno.land/. To learn more about how to configure the linter, check out the deno lint subcommand.

Formatting (optional) Jump to heading

Deno ships with a built-in formatter that can optionally format your code according to the Deno style guide. You can run the formatter on your project by running:

deno fmt

If using deno fmt in CI, you can pass the --check argument to make the formatter exit with an error when it detects improperly formatted code.

deno fmt --check

The formatting rules can be configured in your deno.json file. To learn more about how to configure the formatter, check out the deno fmt subcommand.

Testing (optional) Jump to heading

Deno encourages writing tests for your code, and provides a built-in test runner to make it easy to write and run tests. The test runner is tightly integrated into Deno, so that you don't have to do any additional configuration to make TypeScript or other features work.

my_test.ts
Deno.test("my test", () => {
  // Your test code here
});
deno test

When passing the --watch flag, the test runner will automatically reload when any of the imported modules change.

To learn more about the test runner and how to configure it, check out the deno test subcommand documentation.

Node to Deno Cheatsheet Jump to heading

Node.js Deno
node file.js deno run file.js
ts-node file.ts deno run file.ts
nodemon deno run --watch
node -e deno eval
npm i / npm install deno install
npm install -g deno install -g
npm run deno task
eslint deno lint
prettier deno fmt
package.json deno.json or package.json
tsc deno check ¹
typedoc deno doc
jest / ava / mocha / tap / etc deno test
nexe / pkg deno compile
npm explain deno info
nvm / n / fnm deno upgrade
tsserver deno lsp
nyc / c8 / istanbul deno coverage
benchmarks deno bench

¹ Type checking happens automatically, TypeScript compiler is built into the deno binary.

Built-in Node.js globals Jump to heading

Deno provides a similar set of built-in globals as Node.js, but with some differences. Here are some common ones:

Node.js Deno
process.cwd() Deno.cwd()
process.env.MY_ENV Deno.env.get("MY_ENV")
process.env.MY_ENV = "foo" Deno.env.set("MY_ENV", "foo")
process.platform Deno.build.os
process.arch Deno.build.arch
process.execPath() Deno.execPath()
process.exit(code) Deno.exit(code)

It is also possible to import Node.js modules into your project using the node: specifier. For example:

import process from "node:process";

APIs Jump to heading

Node.js Deno
fsPromises.readFile(filePath, "utf-8") Deno.readTextFile(filePath)

Node Compatibility Jump to heading

Deno provides polyfills for a number of built-in Node.js modules and globals. For a full list of Node built-in modules, see the reference.

Node compatibility is an ongoing project - help us identify gaps and let us know which modules you need by opening an issue on GitHub.

Built-in module support Jump to heading

✅ = Full support
ℹ️ = Partial support
❌ = Stubs only
node:assert

Fully supported.

Reference docs

node:async_hooks
ℹ️

AsyncLocalStorage is supported. AsyncResource, executionAsyncId, and createHook are non-functional stubs.

Reference docs

node:buffer

Fully supported.

Reference docs

node:child_process

Fully supported.

Reference docs

node:cluster

All exports are non-functional stubs.

Reference docs

node:console

Fully supported.

Reference docs

node:crypto

Missing Certificate class, crypto.Cipheriv.prototype.setAutoPadding, crypto.Decipheriv.prototype.setAutoPadding, crypto.publicDecrypt, crypto.ECDH.prototype.convertKey, x448 option for generateKeyPair, crypto.KeyObject, safe, add and rem options for generatePrime, crypto.Sign.prototype.sign and crypto.Verify.prototype.verify with non BinaryLike input, crypto.secureHeapUsed, crypto.setEngine, legacy methods of crypto.X509Certificate.

Reference docs

node:dgram
ℹ️

Some dgram.Socket instance methods are non-functional stubs:

  • addMembership
  • addSourceSpecificMembership
  • dropMembership
  • dropSourceSpecificMembership
  • setBroadcast
  • setMulticastInterface
  • setMulticastLoopback
  • setMulticastTtl
  • setTtl

Reference docs

node:diagnostics_channel

Fully supported.

Reference docs

node:dns
ℹ️

Missing dns.resolve* with ttl option.

Reference docs

node:domain

All exports are non-functional stubs. This is a deprecated Node module.

Reference docs

node:events

Fully supported.

Reference docs

node:fs
node:fs

Missing utf16le, latin1 and ucs2 encoding for fs.writeFile and fs.writeFileSync.

node:fs/promises

Missing lchmod.

Reference docs

node:http

createConnection option is currently not supported.

Reference docs

node:http2
ℹ️

Partially supported, major work in progress to enable grpc-js.

Reference docs

node:https
ℹ️

Missing https.Server.opts.cert and https.Server.opts.key array type.

Reference docs

node:inspector
ℹ️

console is supported. Other APIs are stubs and will throw an error.

Reference docs

node:module

The `register()` function is not supported.

Reference docs

node:net
ℹ️

Missing net.Socket.prototype.constructor with fd option.

Reference docs

node:os

Fully supported.

Reference docs

node:path

Fully supported.

Reference docs

node:perf_hooks
ℹ️

Missing perf_hooks.eventLoopUtilization, perf_hooks.timerify, perf_hooks.monitorEventLoopDelay.

Reference docs

node:punycode

Fully supported.

Reference docs

node:process
ℹ️

Missing multipleResolves, worker events.

Reference docs

node:querystring

Fully supported.

Reference docs

node:readline

Fully supported.

Reference docs

node:repl
ℹ️

builtinModules and _builtinLibs are supported. Missing REPLServer.prototype.constructor and start().

Reference docs

node:stream

Fully supported.

Reference docs

node:string_decoder

Fully supported.

Reference docs

node:sys

Fully supported.

Reference docs

node:test
ℹ️

Currently only test API is supported.

Reference docs

node:timers

Fully supported.

Reference docs

node:tls
ℹ️

Missing createSecurePair.

Reference docs

node:trace_events

All exports are non-functional stubs.

Reference docs

node:tty

Fully supported.

Reference docs

node:util
ℹ️

Missing aborted, transferableAbortSignal, transferableAbortController, MIMEParams, MIMETypeand getSystemErrorMap.

Reference docs

node:url

Fully supported.

Reference docs

node:v8
ℹ️

cachedDataVersionTag and getHeapStatistics are supported. setFlagsFromStrings is a noop. Other APIs are not supported and will throw and error.

Reference docs

node:vm
ℹ️

Partial support.

Reference docs

node:wasi

All exports are non-functional stubs.

Reference docs

node:worker_threads
ℹ️

Missing parentPort.emit, parentPort.removeAllListeners, markAsUntransferable, moveMessagePortToContext, receiveMessageOnPort, Worker.prototype.getHeapSnapshot.

Reference docs

node:zlib

Fully supported.

Reference docs

Globals Jump to heading

This is the list of Node globals that Deno supports. These globals are only available in the npm package scope. In your own code you can use them by importing them from the relevant node: module.

Global name Status
AbortController
AbortSignal
Blob
Buffer
ByteLengthQueuingStrategy
__dirname ⚠️ Info
__filename ⚠️ Info
atob
BroadcastChannel
btoa
clearImmediate
clearInterval
clearTimeout
CompressionStream
console
CountQueuingStrategy
Crypto
CryptoKey
CustomEvent
CustomEvent
DecompressionStream
Event
EventTarget
exports
fetch
fetch
File
File
FormData
global
Headers
MessageChannel
MessageEvent
MessagePort
module
PerformanceEntry
PerformanceMark
PerformanceMeasure
PerformanceObserver
PerformanceObserverEntryList
PerformanceResourceTiming
performance
process
queueMicrotask
ReadableByteStreamController
ReadableStream
ReadableStreamBYOBReader
ReadableStreamBYOBRequest
ReadableStreamDefaultController
ReadableStreamDefaultReader
require
Response
Request
setImmediate
setInterval
setTimeout
structuredClone
structuredClone
SubtleCrypto
DOMException
TextDecoder
TextDecoderStream
TextEncoder
TextEncoderStream
TransformStream
TransformStreamDefaultController
URL
URLSearchParams
URLSearchParams
WebAssembly
WritableStream
WritableStreamDefaultController
WritableStreamDefaultWriter

Unstable compatibility features Jump to heading

Node and npm compatibility is an ongoing project for the Deno team. As such, there are a number of unstable features aimed at improving compatibility that you may want to reference. Please check out the unstable feature flags documentation for options that may improve your project's compatibility with code written for Node.js.