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

Command
Description

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);

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

Command
Description

Add dependencies.

Install a dependency or a script.

Uninstall a dependency or a script.

Remove dependencies.

View or update outdated dependencies.

Tooling

Command
Description

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.

Older versions of Deno used package.json as the configuration file, just like Node.

So, for backward compatibility it can work.

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.

Read more about Deno default Linting and Formatting.

Say goodbye to Prettier.

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:

deno.json
{
    "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.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

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.

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.

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

import { 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";

Deno recommends JSR, the modern JavaScript registry, for third party modules. There, you'll find plenty of well documented ES modules for your projects, including the Deno Standard Library.

In deno.json

To better maintain code you can also map these imports in the "imports" section of deno.json.

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

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

You 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>

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.

Last updated