Deno
Deno can run JavaScript and TypeScript with no additional tools or configuration required, unlike Node
which will require some additional libraries and configurations to work with it.
Additionally Deno by default can work as HTTP Server
, Routes
, Database access
, and more.
Installing Deno
Installing on Linux is simple:
curl -fsSL https://deno.land/install.sh | sh
Check verion with:
deno --version
To interact with the Deno runtime environment from your terminal or command prompt.
Execution
To run scripts.
To run a web server.
To run tasks configured in deno.json
. (Ex.: deno task build)
It is possible to provide runtime arguments to your scripts.
deno run main.ts arg1 arg2 arg3
Inside main.ts
you grab the arguments with:
console.log(Deno.args);
Note that anything passed after the script name will be passed as a script argument and not consumed as a Deno runtime flag.
Supply the --watch
to run
, test
, fmt
to enable the built-in file watcher.
The watcher enables automatic reloading of your application whenever changes are detected on the source files.
(Similar to nodemon
for Node)
Other common flags
Can be checked in the docs.
Dependency Management
Add dependencies.
Install a dependency or a script.
Uninstall a dependency or a script.
Remove dependencies.
View or update outdated dependencies.
Tooling
Upgrade deno
to last version.
Create a new project.
Run your tests.
Generate test coverage reports.
Generate documentation for a module.
Benchmarking tool.
Compile a program into a standalone executable.
Format your code.
Lint your code.
Deno Project
Deno configuration file is deno.json
which is similar to the package.json
from Node.
It will automatically recognize this file in project root folder, but an arbitrary path to the configuration file can be passed through the --config
flag when executing Deno.
If both deno.json
and package.json
are present Deno will understand dependencies specified in both.
Use deno.json
for Deno-specific configurations.
Also check more info in Reloading Modules.
deno.json
Check a full example of the configuration file here.
The "imports"
field in your deno.json
allows you to specify dependencies used in your project.
You can use it to map bare specifiers to URLs or file paths making it easier to manage dependencies and module resolution in your applications.
The "tasks"
field in your deno.json
file is used to define custom commands that can be executed with the deno task
command and allows you to tailor commands and permissions to the specific needs of your project. (Just like "scripts"
from Node)
The "lint"
field in the deno.json
file is used to configure the behavior of Deno’s built-in linter.
This allows you to specify which files to include or exclude from linting, as well as customize the linting rules to suit your project’s needs.
The "fmt"
field in the deno.json
file is used to configure the behavior of Deno’s built-in code formatter.
This allows you to customize how your code is formatted, ensuring consistency across your project, making it easier to read and collaborate on.
The "lock"
field in the deno.json
file is used to specify configuration of the lock file that Deno uses to ensure the integrity of your dependencies.
You can disable Deno from creating the deno.lock
file with:
{
"lock": false
}
Since Deno by default always add new added dependencies to the lockfile, it is also possible to freeze this lockfile (ex. CI pipelines or production environments) so that Deno will error when it encounter dependencies it's never seen before.
To do this you can pass the --frozen
flag or add the configuration to deno.json
.
{
"lock": {
"frozen": true
}
}
The "compilerOptions"
field in the deno.json
file is used to configure TypeScript compiler settings for your Deno project.
Many of the configurations in deno.json
can accept include
and exclude
properties.
Check more detailed info on the docs.
Node and Npm Support
Node.js
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.
And run it with deno run main.mjs
.
import * as os from "node:os";
console.log(os.cpus());
Npm
Deno has native support for importing npm packages by using npm:
specifiers. For example:
import * as emoji from "npm:node-emoji";
console.log(emoji.emojify(`:sauropod: :heart: npm`));
No npm install
is necessary before the deno run
command and no node_modules
folder is created.
Importing types
Many npm packages ship with types, you can import these and use them with types directly:
import chalk from "npm:chalk@5";
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
Unless specifically enabled, a Deno script has no access to sensitive APIs like file system access
, network connectivity
, or environment access
.
To do so, it is necessary to grant access to these resources with command line flags or with a runtime permission prompt.
This is a major difference from Node, where dependencies are automatically granted full access to all system I/O, potentially introducing hidden vulnerabilities into your project.
Key Principles
No access to I/O by default.
Code executing in a Deno runtime has no access to read or write arbitrary files on the file system, to make network requests or open network listeners, to access environment variables, or to spawn subprocesses.
No limits on the execution of code at the same privilege level.
Multiple invocations of the same application can share data.
Deno provides a mechanism for multiple invocations of the same application to share data, through built in caching and KV storage APIs. Different applications can not see each other's data.
All code executing on the same thread shares the same privilege level.
Code can not escalate its privileges without user consent.
The initial static module graph can import local files without restrictions.
Permissions
By default, access to most system I/O is denied. There are some I/O operations that are allowed in a limited capacity, even by default.
To enable these operations, the user must explicitly grant permission to the Deno runtime. This is done by passing flags to the deno
command:
Also, during script execution, a user can grant permissions when prompted by the runtime. (Prompts are not shown if stdout/stderr are not a TTY)
Reloading Modules
By default, Deno uses a global cache directory (DENO_DIR
) for downloaded dependencies. This cache is shared across all projects.
Have this in mind when starting projects:
If project only have
deno.json
file,node_modules
folder will NOT be created and dependencies will be downloaded to this global cache.It may be possible to force
deno.json
to createnode_modules
check more at docs.
If project have
deno.json
andpackage.json
files,node_modules
is created and dependencies are downloaded to it.
You can force deno to refetch and recompile modules into the cache using the --reload
flag.
# Reload everything
deno run --reload my_module.ts
# Reload a specific module
deno run --reload=jsr:@std/fs my_module.ts
Third party packages
Can be imported with jsr
, npm
and (https
and http
).
Directly in the .ts
files
.ts
filesimport { camelCase } from "jsr:@luca/cases@1.0.0";
import { say } from "npm:cowsay@1.6.0";
import { pascalCase } from "https://deno.land/x/case/mod.ts";
In deno.json
deno.json
To better maintain code you can also map these imports in the "imports"
section of deno.json
.
{
"imports": {
"@luca/cases": "jsr:@luca/cases@^1.0.0",
"cowsay": "npm:cowsay@^1.6.0",
"cases": "https://deno.land/x/case/mod.ts"
}
}
import { camelCase } from "@luca/cases";
import { say } from "cowsay";
import { pascalCase } from "cases";
With deno add
deno add
An even better way is to use the CLI to import these packages. It will automatically add them to the deno.json
as "imports"
and get the latest versions.
deno add jsr:@luca/cases
It is possible to be specific in what versions of packages you may want to import.
@scopename/mypackage # highest version
@scopename/mypackage@16.1.0 # exact version
@scopename/mypackage@16 # highest 16.x version >= 16.0.0
@scopename/mypackage@^16.1.0 # highest 16.x version >= 16.1.0
@scopename/mypackage@~16.1.0 # highest 16.1.x version >= 16.1.0
Deno can also load modules from private repositories like on Github.
Deno supports sending bearer tokens when requesting a remote module.
Check more info in the docs.
Deno provides a standard library written in TypeScript.
This standard library provide some packages for dealing with json
, path
, and many other.
The standard library is hosted on JSR and is available at: https://jsr.io/@std.
To install packages from the Deno Standard Library, you can use the deno add
subcommand to add the package to your deno.json
import map.
deno add jsr:@std/fs jsr:@std/path
import { copy } from "@std/fs";
import { join } from "@std/path";
There is built-in support for environment variables with Deno.env
.
There are getter
and setter
methods.
Deno.env.set("FIREBASE_API_KEY", "examplekey123");
Deno.env.set("FIREBASE_AUTH_DOMAIN", "firebasedomain.com");
console.log(Deno.env.get("FIREBASE_API_KEY")); // examplekey123
console.log(Deno.env.get("FIREBASE_AUTH_DOMAIN")); // firebasedomain.com
console.log(Deno.env.has("FIREBASE_AUTH_DOMAIN")); // true
Special Deno Environment Variables
Check here.
.env
Files
.env
FilesYou can cause Deno to read environment variables from .env
using the --env-file
flag.
This will read .env
by default.
deno run --env-file <script>
If you want or need to load environment variables from a different file, you can specify that file as a parameter to the flag.
deno run --env-file=.env.one --env-file=.env.two --allow-env <script>
When multiple declarations for the same environment variable exist within a single .env
file, the first occurrence is applied.
However, if the same variable is defined across multiple .env
files (using multiple --env-file
arguments), the value from the last file specified takes precedence.
Deno have its own Test library, so there is no need to use external tools like Jest
or others.
Tests structures
Simple test
Deno.test('', () => {});
Test steps
Deno also supports test steps, which allow you to break down tests into smaller, manageable parts.
Deno.test("database operations", async (t) => {
using db = await openDatabase();
await t.step("insert user", async () => {
// Insert user logic
});
await t.step("insert book", async () => {
// Insert book logic
});
});
Assertions
Deno provides a function assertEqual
and others for asserting expected values.
But it also provides an assert function like Jest
expect
.
Running tests
Run deno test
without a file name or directory name, this subcommand will automatically find and execute all tests in the current directory (recursively).
# Run all tests in the current directory and all sub-directories
deno test
# Run all tests in the util directory
deno test util/
# Run just my_test.ts
deno test my_test.ts
# Run test modules in parallel
deno test --parallel
# Pass additional arguments to the test file that are visible in `Deno.args`
deno test my_test.ts -- -e --foo --bar
# Provide permission for deno to read from the filesystem, which is necessary
# for the final test above to pass
deno test --allow-read my_test.ts
Check the docs for more info on debugging with VSCode.
Examples
Bellow some examples on doing things with Deno.
Files
Network
Subprocess
Error Handling
Permissions
Storage
NodeJs Modules
Continuos Integration (CI)
Last updated