On this page
Node.js support
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.
Node built-in modules 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:
import * as os from "node:os";
console.log(os.cpus());
And run it with deno run main.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.
To make updating existing code easier, Deno will provide helpful hints for
imports that don't use node:
prefix:
import * as os from "os";
console.log(os.cpus());
$ deno run main.mjs
error: Relative import path "os" not prefixed with / or ./ or ../
hint: If you want to use a built-in Node module, add a "node:" prefix (ex. "node:os").
at file:///main.mjs:1:21
Same hints and additional quick-fixes are provided by the Deno LSP in your editor.
CommonJS support Jump to heading
CommonJS is a module system that predates ES modules. While we firmly believe that ES modules are the future of JavaScript, there are millions of npm libraries that are written in CommonJS and Deno offers full support for them. Deno will automatically determine if a package is using CommonJS and make it work seamlessly when imported:
import react from "npm:react";
console.log(react);
$ deno run -E main.js
18.3.1
npm:react
is a CommonJS package. Deno allows to import it as it was an ES
module.
Deno strongly encourages use of ES modules in your code, and offers CommonJS support with following restrictions:
Use .cjs
extension Jump to heading
If the file extension is .cjs
Deno will treat this module as CommonJS.
const express = require("express");
Deno does not look for package.json
files and type
option to determine if
the file is CommonJS or ESM.
When using CommonJS, Deno expects that dependencies will be installed manually
and a node_modules
directory will be present. It's best to set
"nodeModulesDir": "auto"
in your deno.json
to ensure that.
$ cat deno.json
{
"nodeModulesDir": "auto"
}
$ deno install npm:express
Add npm:express@5.0.0
$ deno run -R -E main.cjs
[Function: createApplication] {
application: {
init: [Function: init],
defaultConfiguration: [Function: defaultConfiguration],
...
}
}
-R
and -E
flags are used to allow permissions to read files and environment
variables. permissions.
Deno's permission system is still in effect when using CommonJS modules. It
is necessary to provide at least --allow-read
permission as Deno will probe
the file system for package.json
files and node_modules
directory to
properly resolve CommonJS modules.
Create require()
manually Jump to heading
An alternative option is to create an instance of the require()
function
manually:
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
const express = require("express");
In this scenario the same requirements apply, as when running .cjs
files -
dependencies need to be installed manually and appropriate permission flags
given.
require(ESM) Jump to heading
Deno's require()
implementation support requiring ES modules.
This works the same as in Node.js, where you can only require()
ES modules
that don't have Top-Level Await in their module graph - or in other words you
can only require()
ES modules that are "synchronous".
export function greet(name) {
return `Hello ${name}`;
}
import { greet } from "./greet.js";
export { greet };
const esm = require("./esm");
console.log(esm);
console.log(esm.greet("Deno"));
$ deno run -R main.cjs
[Module: null prototype] { greet: [Function: greet] }
Hello Deno
import "./index.cjs" Jump to heading
You can also import CommonJS files in ES modules, provided that these files use
.cjs
extension.
Deno does not look for package.json
files and type
option to determine if
the file is CommonJS or ESM.
module.exports = {
hello: "world",
};
import greet from "./greet.js";
console.log(greet);
$ deno run main.js
{
"hello": "world"
}
Notice that in this example no permission flags were specified - when importing CJS from ES modules, Deno can staticaly analyze and find relevant modules without having to probe file system at runtime.
Hints and suggestions Jump to heading
Deno will provide useful hints and suggestions to guide you towards working code when working with CommonJS modules.
As an example, if you try to run a CommonJS module that doesn't have .cjs
extension you might see this:
module.exports = {
hello: "world",
};
$ deno run main.js
error: Uncaught (in promise) ReferenceError: module is not defined
module.exports = {
^
at file:///main.js:1:1
info: Deno does not support CommonJS modules without `.cjs` extension.
hint: Rewrite this module to ESM or change the file extension to `.cjs`.
Node.js global objects Jump to heading
In Node.js, there are a number of
global objects available in the scope of
all programs that are specific to Node.js, eg. process
object.
Here are a few globals that you might enounter in the wild and how to use them in Deno:
process
- Deno provides theprocess
global, which is by far the most popular global used in popular npm packages. It is available to all code. However, Deno will guide you towards importing it explicitly fromnode:process
module by providing lint warnings and quick-fixes:
console.log(process.versions.deno);
$ deno run process.js
2.0.0
$ deno lint process.js
error[no-process-globals]: NodeJS process global is discouraged in Deno
--> /process.js:1:13
|
1 | console.log(process.versions.deno);
| ^^^^^^^
= hint: Add `import process from "node:process";`
docs: https://lint.deno.land/rules/no-process-globals
Found 1 problem (1 fixable via --fix)
Checked 1 file
-
require()
- see CommonJS support -
Buffer
- to useBuffer
API it needs to be explicitly imported from thenode:buffer
module:
import { Buffer } from "node:buffer";
const buf = new Buffer(5, "0");
Prefer using
Uint8Array
or other
TypedArray
subclasses instead.
-
__filename
- useimport.meta.filename
instead. -
__dirname
- useimport.meta.dirname
instead.
Node-API addons Jump to heading
Deno supports Node-API addons that are used
by popular npm packages like esbuild
,
npm:sqlite3
or
npm:duckdb
.
You can expect all packages that use public and documented Node-APIs to work.
Most packages using Node-API addons rely on npm "lifecycle scripts", like
postinstall
.
While Deno supports them, they are not run by default due to security
considerations. Read more in
deno install
docs.
As of Deno 2.0, npm packages using Node-API addons are only supported when a
node_modules/
directory is present. Add "nodeModulesDir": "auto"
or
"nodeModulesDir": "manual"
setting your deno.json
file, or run with
--node-modules-dir=auto|manual
flag to ensure these packages work correctly.
In case of misconfiguration Deno will provide hints how the situation can be
resolved.
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
:
{
"name": "my-project",
"scripts": {
"start": "eslint"
}
}
You can execute this script with Deno by running:
deno task start
Migrating from Node.js to Deno Jump to heading
Running your Node.js project with Deno is a straightforward process. In most cases you can expect little to no changes to be required, if your project is written using ES modules.
Main points to be aware of, include:
- Importing Node.js built-in modules requires the
node:
specifier:
// ❌
import * as fs from "fs";
import * as http from "http";
// ✅
import * as fs from "node:fs";
import * as http from "node:http";
It is recommended to change these import specifiers in your existing project anyway. This is a recommended way to import them in Node.js too.
- Some globals available in Node.js need to be
explicitly imported, eg.
Buffer
:
import { Buffer } from "node:buffer";
require()
is only available in files with.cjs
extension, in other files an instance ofrequire()
needs to be created manually. npm dependencies can userequire()
regardless of file extension.
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 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 dependencies
using the imports
option - you can migrate your dependencies one-by-one from
package.json
, or elect to not define them in the config file at all and use
npm:
specifiers inline in your code.
In addition to specifying depenendencies you can use
deno.json
to define tasks, lint and
format options, path mappings, and other runtime configurations.
Linting Jump to heading
Deno ships with a built-in linter that is written with performance in mind. It's
similar to ESlint, though with a limited number of rules. If you don't rely on
ESLint plugins, you can drop eslint
dependency from devDependencies
section
of package.json
and use deno lint
instead.
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 Jump to heading
Deno ships with a built-in formatter that can optionally
format your code according to the Deno style guide. Instead of adding prettier
to your devDependencies
you can instead use Deno's built-in zero-config code
formatter deno fmt
.
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 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.
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 file.js |
ts-node file.ts |
deno 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.
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.
AsyncResource: The AsyncResource implementation is a non-functional stub.
executionAsyncId: The executionAsyncId implementation is a non-functional stub.
createHook: The createHook implementation is a non-functional stub.
All exports are non-functional stubs.
All symbols: This symbol is a non-functional stub.
Certificate: The methods are non-functional stubs.
generateKeyPair: The x448
option is not supported.
generatePrime: The safe
, add
and rem
option is not supported.
KeyObject: The methods are non-functional stubs.
publicDecrypt: This symbol is a non-functional stub.
secureHeapUsed: This symbol is a non-functional stub.
setEngine: This symbol is a non-functional stub.
ECDH: The convertKey
method is a non-functional sub.
Sign: The sign
and verify
methods are not supported with non BinaryLike input.
Socket: The following methods are non-functional stubs:
- addMembership
- addSourceSpecificMembership
- dropMembership
- dropSourceSpecificMembership
- setBroadcast
- setMulticastInterface
- setMulticastLoopback
- setMulticastTtl
- setTtl
resolve: The ttl
option is not supported.
resolve4: The ttl
option is not supported.
resolve6: The ttl
option is not supported.
resolveCname: The ttl
option is not supported.
resolveCaa: The ttl
option is not supported.
resolveMx: The ttl
option is not supported.
resolveNaptr: The ttl
option is not supported.
resolveNs: The ttl
option is not supported.
resolvePtr: The ttl
option is not supported.
resolveSoa: The ttl
option is not supported.
resolveSrv: The ttl
option is not supported.
resolveTxt: The ttl
option is not supported.
resolveAny: The ttl
option is not supported.
All exports are non-functional stubs. This is a deprecated Node module.
All symbols: This symbol is a non-functional stub.
writeFile: Missing utf16le
, latin1
and ucs2
encoding.
writeFileSync: Missing utf16le
, latin1
and ucs2
encoding.
lchmod: The lchmod implementation is a non-functional stub.
RequestOptions: Option createConnection
is not supported.
ClientRequestArgs: Option createConnection
is not supported.
ClientRequest: Constructor option createConnection
is not supported.
request: Constructor option createConnection
is not supported.
get: Constructor option createConnection
is not supported.
Partially supported, major work in progress to enable grpc-js
.
Server: The cert
and key
options do not support an array input.
console
is supported. Other APIs are non-functional stubs and will throw an error.
Module: The register
method is not supported.
Socket: The fd
option is not supported.
performance: The eventLoopUtilization
method is a non-functional stub.
The timerify
method is not implemented.
monitorEventLoopDelay: This symbol is not implemented.
The multipleResolves
and worker
events are not supported.
REPLServer: This symbol is not supported.
start: This symbol is not supported.
Currently only test
API is supported.
createSecurePair: This symbol is currently not supported.
All exports are non-functional stubs.
All symbols: This symbol is a non-functional stub.
transferableAbortSignal: This symbol is currently not supported.
transferableAbortController: This symbol is currently not supported.
MIMEParams: This symbol is currently not supported.
MIMEType: This symbol is currently not supported.
getSystemErrorMap: This symbol is currently not supported.
cachedDataVersionTag
and getHeapStatistics
are supported.
setFlagsFromStrings
is a noop.
Other APIs are not supported and will throw and error.
setFlagsFromStrings: This function is a noop.
This module is supported only partially.
parentPort: The emit
method is not supported.
The removeAllListeners
method is not supported.
markAsUntransferable: This symbol is not supported.
moveMessagePortToContext: This symbol is not supported.
receiveMessageOnPort: This symbol is not supported.
Worker: The getHeapSnapshot
method is not supported.
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.