Introduction to Deno

Table of contents

What is Deno

Deno is a new Javascript/Typescript runtime. Version 1.0.0 was released in May 2020.

It can be thought of as what NodeJS would be like if it was designed in 2020! It was created by Ryan Dahl who was the original creator of NodeJS.

Back in 2018, he gave a speech covering the things that he regretted the most about NodeJS.

Differences between Node and Deno

  • Unlike Node, it does not support require(). Instead, all imports are done with ES Modules such as import * as something from "http://example.com/something.ts". (more on this further down the page)
  • package.json is not a thing for Deno for module resolution.
  • Node has NPM. Deno does not! Dependencies are included via full URLs which the runtime will download (and cache)
  • Deno is by default heavily sandboxed (like your browser is) - by default it can't access the web or the filesystem.
  • Unlike Node which uses callbacks everywhere, Deno uses promises for async actions. This is a huge move towards a much cleaner, nicer and more modern way of coding.

How dependencies and package management works in Deno

  • There is no use of NPM (or yarn)! No package.json!
  • There is no requirement for there to be a central place to hold all of your dependencies (see next section though). Instead, each file that has an import will directly reference it with a full URL.
  • Example of an import: import { assertEquals } from "https://deno.land/std/testing/asserts.ts";

How to keep dependencies organised in Deno

You can do relative imports (like you do right now in node: require('./models/user.js')). I would recommend that you create one main file with all dependencies in it, then export them from that file (self-contained dependency list). Then in other files, you can import from that file.

For example, in /src/deps.ts (which you can treat as the source for all of your dependencies, a bit like package.json):

// Your self contained dependency list of urls your entire project needs:
import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
import { somethingElse } from "https://some-other-dependency.com/index.ts";
// ... and so on

// Export them so other files can easily access them:
export default const {
  assertEquals,
  somethingElse,
};

Then in your files which need to use assertEquals:

// import from your self contained dependency list file:
import { assertEquals } from "./deps.ts";

// use it as you need!
assertEquals('something','something');

This has several benefits including the fact that you have one central location with all of your external dependencies listed (so updating is easy).

Can you use lock files for dependencies in Deno?

Deno can store and check module subresource integrity for modules using a small JSON file. Use the --lock=lock.json to enable and specify lock file checking. To update or create a lock use --lock=lock.json --lock-write.

See here for more details.

Can you use normal npm packages in Deno?

The short answer is no, you cannot you normal npm packages in Deno. However, there are some easy workarounds.

To work with Deno one of the major hurdles with a lot of existing packages is that it requires ES modules (import x from y) and all imports must have full extensions (e.g. .js). Most packages on NPM use require (const lodash = require('lodash')), and very few include the file extension.

An easy way to import any NPM package in deno is to use Pika CDN. Every npm package can be loaded from Pika CDN as a modern ESM import. If a package wasn't written as ESM, we'll do the work to convert it for you.

An example of loading from pika:

import {Component, render} from 'https://cdn.pika.dev/preact@^10.0.0';

Typescript support

Deno supports Javascript and Typescript, both as first-class languages. Even if your current code is only in Javascript, you can start using Typescript straight away. There is no need to think about setting everything up to compile from TyepScript to JavaScript - Deno handles it all for you.

All imports must include the full extension (.js or .ts), as it will not try and guess them when resolving modules.

You can still set up Typescript as normal with a tsconfig.json file.

To run a file with a custom ts config, run deno run -c tsconfig.json some_file.ts

The default Typescript configuration in Deno is as follows:

{
  "compilerOptions": {
    "allowJs": false,
    "allowUmdGlobalAccess": false,
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "alwaysStrict": true,
    "assumeChangesOnlyAffectDirectDependencies": false,
    "checkJs": false,
    "disableSizeLimit": false,
    "generateCpuProfile": "profile.cpuprofile",
    "jsx": "react",
    "jsxFactory": "React.createElement",
    "lib": [],
    "noFallthroughCasesInSwitch": false,
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noImplicitUseStrict": false,
    "noStrictGenericChecks": false,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "preserveConstEnums": false,
    "removeComments": false,
    "resolveJsonModule": true,
    "strict": true,
    "strictBindCallApply": true,
    "strictFunctionTypes": true,
    "strictNullChecks": true,
    "strictPropertyInitialization": true,
    "suppressExcessPropertyErrors": false,
    "suppressImplicitAnyIndexErrors": false,
    "useDefineForClassFields": false
  }
}

Installing Deno

Installing Deno on your computer is easy.

If you are on macOS or Linux then run the following from the command line:

curl -fsSL https://deno.land/x/install/install.sh | sh

You can also use Brew to install Deno on macOS:

brew install deno

How to update Deno

It is possible to update Deno by running deno upgrade. Use deno --version to check what version of Deno you are running.

Running Deno commands

Deno comes with a bunch of commands. You can see all of them by running deno --help in your terminal.

Some of the most useful commands include:

  • deno bundle

    • This will combine JS and dependencies of a project into a single file
  • deno cache

    • This will download and compile a module (with all of its static dependencies) and save them in the local cache. No code will be executed.
  • deno completions

    • Outputs a shell completion script. Use it with something like deno completions bash > /usr/local/etc/bash_completion.d/deno.bash (then load it with source /usr/local/etc/bash_completion.d/deno.bash)
  • deno doc

    • Use deno doc to show documention for a module.
    • You can use it like deno doc ./path/to/module.ts
    • For JSON output deno doc --json ./path/to/module.ts
  • deno eval

    • Use deno eval to run Javascript.
    • Example: deno eval "console.log('hello from webdevetc.com')"
    • You can also use it for running TypeScript but it requires an additional argument of -T: deno eval -T "const v: string = 'hello'; console.log(v)"
  • deno fmt

    • Format JavaScript/TypeScript automatically.
    • To ignore formatting some code, add // deno-fmt-ignore above it.
    • To ignore an entire file, add // deno-fmt-ignore-file at the start of the file.
    • Usage example: deno fmt, deno fmt myfile1.ts myfile2.ts
    • To check for formatting without writing: deno fmt --check
    • If you have ever used Go lang, then you will be familiar with Go's fmt function. This is very similar.
  • deno help

    • Show all available commands and some debug info
  • deno info

    • Use deno info to get information about a module.
    • Can use it with urls, such as deno info https://deno.land/std/http/file_server.ts
  • deno install

    • Installs a script locally, as an executable.
    • deno install --allow-net --allow-read https://deno.land/std/http/file_server.ts
    • deno install https://deno.land/std/examples/colors.ts
    • You can use arguments such as --allow-net or --allow-read to give permissions to break out of the sandbox.
    • deno install --allow-net --allow-read -n serve https://deno.land/std/http/file_server.ts
  • deno repl

    • Use deno repl to enter the "Read-Eval-Print-Loop" mode.
    • This lets you type Javascript/TypeScript and it will be executed straight away.
    • Great for testing things out.
    • You can use the --v8-flags=<v8-flats> option for specific v8 engine flags
  • deno run

    • Use deno run with a filename or URL as an argument to run it.
    • By default, it will run in a sandbox. Use things such as --allow-read or --allow-net to give extra permissions.
    • Use -A to give all permissions.
  • deno test

    • Run tests using the built-in test runner.
    • It will run all tests declared with Deno.test() and output to standard output.
    • By default matches {*_,}test.{js,ts,jsx,tsx}
  • deno types

    • output type declaration file.
    • Use it like deno types > lib.deno.d.ts
  • deno upgrade

    • As mentioned previously, this will upgrade Deno to the latest version

The Deno Sandbox & Permissions

The Deno sandbox is one of it's greatest features. Deno's sandbox is similar to your web browser's Javascript sandbox, which prevents accessing you files on your computer.

By default, the sandbox is enabled, which means Deno is very restricted in what it can do.

The following restrictions can be removed by allowing certain actions:

  • -A or --allow-all - Allow all permissions (use with caution!)
  • --allow-env - Allow environment access
  • --allow-hrtime - Allow high-resolution time measurement
  • --allow-net - Allow network access

    • You can set specific restrictions with --allow-net=
  • --allow-plugin - Allow loading plugins
  • --allow-read - Allow file system read access

    • You can set specific restrictions, such as allowing access only to /var/log with deno --allow-read=/var/log
  • --allow-run - Allow running subprocesses
  • --allow-write - Allow file system write access

    • You can optionally specify what directories it can write to (similar to --allow-read)

(Node had no such restrictions, and could access any files, make any network request etc).

Hello World in Deno

The most basic of a Hello World example would be a file containing just console.log("Hello world!").

To see something with a little more going on, here is a very basic web server in Deno which will output 'Hello World':

import { serve } from "https://deno.land/std@0.50.0/http/server.ts";
const s = serve({ port: 8000 });
console.log("http://localhost:8000/");
for await (const req of s) {
  req.respond({ body: "Hello World\n" });
}

More complex code examples in Deno

Reading files in Deno

The following example would work when run from the command line. Deno.args are the arguments provided when running the file.

for (let i = 0; i < Deno.args.length; i++) {
  let filename = Deno.args[i];
  let file = await Deno.open(filename);
  await Deno.copy(file, Deno.stdout);
  file.close();
}

Testing in Deno

Deno has a fantastic built-in test runner that you can use to test your JavaScript or TypeScript code.

Examples of testing in Deno

Deno.test("hello world", () => {
  const x = 1 + 2;
  if (x !== 3) {
    throw Error("x should be equal to 3");
  }
});

It is more common to use their testing utility package, which includes helpers such as assertEquals(). An example of that in use (along with the import) follows:

import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
Deno.test("hello world", () => {
  const x = 1 + 2;
  assertEquals(x, 3);
});

Running the tests in Deno

To run tests in Deno you can use the deno test command:

deno test my_test.ts

The filename (my_test.ts in the above example) is optional. If you do not include it then all tests in the current directory (recursively) that match the glob {*_,}test.{js,ts,jsx,tsx} will be run.

If you pass a directory as then it will test all files in the directory that match {*_,}test.{js,ts,jsx,tsx}.

Web standard API (such as fetch)

Although packages, libraries and frameworks are invented on a nearly daily basis in the Javascript world, the APIs provided by web standards are set in stone and will be around for a long time. So Deno has implemented many of them, so your code can be identical on Deno as it is for browsers.

Some of the commons ones that you should be aware of include:

  • addEventListener
  • clearInterval
  • clearTimeout
  • dispatchEvent
  • fetch
  • removeEventListener
  • setInterval
  • setTimeout
  • onload
  • onunload
  • self
  • window

The Deno Standard Library

As well as providing the Deno runtime, there is also a large official standard library. The creators of Deno put a huge amount of effort into keeping the standard library up to a very high standard.

You can see where they are hosted at deno.land/std.

These includes packages such as:

  • testing - see the section on testing in this guide
  • fs (filesystem) - all using promises so you can use await with them
  • hash (for hashing data)
  • datetime to handle date and time
  • and many more

Where to go next

The first place you should go is DenoLand - the official home of Deno. You can read their full installation guide and get up and running in a few minutes.

Next, you should read through their easy to follow manual. Then start building things!

Please leave a comment below if you have any suggestions.

Comments Introduction to Deno