left-icon

Deno Succinctly®
by Mark Lewin

Previous
Chapter

of
A
A
A

CHAPTER 2

Deno and Node.js

Deno and Node.js


In Chapter 1, we took a brief look at the (short) history of Deno and some of the rationale behind its development. In this chapter, I’d like to do a bit of a deeper dive into how Deno differs from Node.js and how I see the future of both runtimes unfolding.

If you’ve never developed in Node, don’t worry. This chapter will still be useful to you because it will teach you the key concepts that you need to know to be able to write and run your applications. We’ll be looking at:

  • How Deno handles dependencies using modules.
  • What APIs are available to Deno.
  • Why your program needs explicit permissions to perform certain tasks, and how to grant those permissions.
  • What tools Deno includes out of the box to help you write, test, and run your code.

Dependencies

Let’s start off by talking about the difference in how Deno manages modules and dependencies, which is one of the most radical differences between Deno and Node.

When Node was invented in 2009, JavaScript didn’t have a standard module system. Incredible as it might seem now, there was no real way to directly reference or include one JavaScript file within another. To do it, developers had to rely either on HTML <script> tags or “bundlers” like Babel, Grunt, and webpack.

Node wanted modules to make it easy for developers to share code, so Node adopted CommonJS for this purpose—one of several workarounds that the community came up with for JavaScript’s lack of modules.

Ultimately, this led to the development of Node’s package management system, which enabled developers to discover, use, and publish modules in a centralized repository. The Node Package Manager (npm) is incredibly well-supported, with over 1.5 million modules at the time of writing, and is arguably one of the key reasons why Node has been so widely adopted.

And Deno does away with it.

“What?” I hear you say. It’s true—Deno has no package manager. It adopts an entirely different approach to Node for working with modules that does not require one.

You see, things have changed a lot in the world of JavaScript since 2009. And one of the biggest changes is the addition of ES6 modules. ES6 modules let you import from either an absolute or relative URL:

Code Listing 19

import { something } from https://somewhere.com/my_module.js;

This relies on the script referenced by that URL using an export function to make the imported functions or other values available externally. For example:

Code Listing 20

export function something() {
  // does stuff
}

This is the approach Deno uses. On the face of it, this might not seem massively different from how CommonJS works, except that CommonJS modules and their dependencies are loaded on demand from the file system while your code is running. ES6 modules are parsed and any dependencies are downloaded before your code executes.

Note: Just to be clear, it’s also possible to use ES6 modules in Node.js these days. But it’s a bit complex because Node must support CommonJS modules at the same time, so it involves using different file extensions to differentiate between the two, and it’s still in the experimental stages. 

Whenever an import statement is encountered by a script, Deno downloads the required module, compiles it, and caches it in a global directory that all Deno programs running on that machine can access. The location of this is your system’s cache directory:

  • On Linux/Redox: $XDG_CACHE_HOME/deno or $HOME/.cache/deno
  • On Windows: %LOCALAPPDATA%/deno (%LOCALAPPDATA% = FOLDERID_LocalAppData)
  • On macOS: $HOME/Library/Caches/deno
  • If something fails, it falls back to $HOME/.deno

This means that only one instance of a module is ever present on your server, even if dozens of programs need it, and that module is available before your code runs.

Tip: If ever you need to force Deno to re-fetch an import, you can do it by executing deno cache --reload my_module.ts.

Compare this to how Node.js does things. Every Node program has a node_modules folder, and all your dependencies are installed there. If you’ve worked with Node much in the past, you will have noticed that node_modules can quickly get out of hand: every module your program requires has dependencies on specific versions of other modules, and this can lead to node_modules becoming very large—sometimes several hundred megabytes:

node_modules can get rather large. Source

Figure 4: node_modules can get rather large. Source: Ryan Dahl’s talk, 10 Things I Regret About Node,js, at JSConf EY 2018.

But what happens if a module URL suddenly becomes unavailable? With Node.js, you would normally just check node_modules into GitHub so that mission-critical applications can continue to run.

Well, you can do that with Deno too. All you need to do is set the DENO_DIR environment variable to a directory within your project. For example:

OS X/Linux:

Code Listing 21

DENO_DIR= ~/myproject/deno_modules

Windows cmd shell:

Code Listing 22

set DENO_DIR="C:\myproject#deno_modules"

Windows PowerShell:

Code Listing 23

$env:DENO_DIR="C:\myproject\deno_modules"

Deno will cache modules in that directory when your program executes, and you can check it into your version control system.

How do I find modules?

So, no package manager? 

Yes, that’s right. No package manager. There are advantages and disadvantages to that, in my opinion. Certainly, it’s very flexible: you can create packages and share them via any URL you like without having to publish them to a centralized repository. But discovering new packages won’t be as easy without one. 

The closest thing Deno has to a centralized repository for packages is at https://deno.land. Packages are stored in two discrete areas:

  • The Deno Standard Library: This is a set of standard modules that are audited by the core team and guaranteed to work with Deno. It includes modules like http (a lightweight server for files over HTTP) and fs (for working with the file system).
  • Third-Party Modules: This a hosting service for Deno scripts. It caches releases of open-source modules stored on GitHub and serves them via a single, easy-to-remember domain. Many of the modules here are direct ports of popular Node.js modules, like lodash (a utility library for working with JavaScript and widely adopted) and oak (an implementation of Node’s Express web framework that we will be looking at in a later chapter).

We’ll be using several modules from both sources in the practical examples in subsequent chapters. 

How are modules versioned?

At the time of writing this, Deno itself is versioned differently from the modules in the standard library, which is not yet considered stable. However, a new version of the standard library is released every time a new version of Deno is released.

The Deno core team recommends that you refer to specific versions of modules rather than the master branch to avoid your application breaking if there are changes to its dependencies.

So, in production, avoid this:

Code Listing 24

import { copy } from "https://deno.land/std/fs/copy.ts";

Instead, refer to a specific version (in this example, version 0.71.0):

Code Listing 25

import { copy } from "https://deno.land/[email protected]/fs/copy.ts";

Deno’s third-party modules in https://deno.land/x work in the same way.

Security

As we have already established, Deno is secure by default. Everything runs in a sandbox without permissions to the network or file system unless you explicitly grant them. Compare this to Node, where your programs have access to everything.

To grant your Deno program access to the network, execute it with the --allow-net argument, like this:

Code Listing 26

deno run --allow-net myprogram.ts

To enable access to the network and reading local files, grant those permissions using two arguments, like this:

Code Listing 27

deno run --allow-net --allow-read myprogram.ts

The other permissions available are shown in Table 1.

Table 1: Deno run permissions

Permission

Enables

-A, –-allow-all

All permissions. The equivalent to disabling all security.

–-allow-env

Getting and setting environment variables.

--allow-hrtime

High-resolution time measurement (to help with timing attacks and fingerprinting).

--allow-net

Network access. Optionally accepts a comma-separated whitelist of domains.

--allow-plugin

Loading plugins (unstable at the time of writing).

--allow-read

File system write access.

--allow-run

Executing subprocesses.

--allow-write

File system read access.

Deno tools

Deno ships with a bunch of command-line tools to make your life as a developer easier. Doubtless you will have used tools like these in your Node development efforts too, but most of them would have been from third parties.

Deno gives you just about everything you need to be productive right out of the box, including the following:

  • Bundler: Bundles the script you specify and its dependencies into a single file.
  • Formatter: Beautifies your TypeScript and JavaScript code in a consistent way, which is especially useful for teams and stops the “tabs versus spaces” arguments that a lot of teams are wont to have.
  • Linter: A code linter that helps you catch errors and inconsistencies in your code.
  • Dependency Inspector: Specify a module, and it shows you a dependency tree. Super helpful.
  • Debugger: Debug your Deno programs with Chrome DevTools, VS Code, and other tools (Node also provides a debugger).
  • Documentation Generator: If you provide JSDoc annotations in your code files, this will parse them and produce documentation.
  • Test Runner: Run tests on your code. Uses the assertions module in the standard library.

You can see all the tools available by executing:

Code Listing 28

deno help

Use deno help <tool> to learn how to use a specific tool. For example, the formatter:

Code Listing 29

$ deno help fmt
deno-fmt
Auto-format JavaScript/TypeScript source code.
  deno fmt
  deno fmt myfile1.ts myfile2.ts
  deno fmt --check

Format stdin and write to stdout:
  cat file.ts | deno fmt -

Ignore formatting code by preceding it with an ignore comment:
  // deno-fmt-ignore

Ignore formatting a file by adding an ignore comment at the top of the file:
  // deno-fmt-ignore-file

USAGE:
    deno fmt [OPTIONS] [files]...

OPTIONS:
        --check                    Check if the source files are formatted
    -h, --help                     Prints help information
        --ignore=<ignore>          Ignore formatting particular source files. Use with --unstable
    -L, --log-level <log-level>    Set log level [possible values: debug, info]
    -q, --quiet                    Suppress diagnostic output
        --unstable                 Enable unstable features and APIs

ARGS:
    <files>...

Deno: the end of Node?

I’ll try and introduce more features of Deno as we use them in subsequent chapters, but I wanted to highlight some of the key differences between it and Node so that you have some idea of what to expect.

Really though, if you know Node, you’re going to appreciate and hopefully one day, fall in love with Deno. Even if you don’t know Node, then learning Deno could well be a valuable investment of your time as it continues to mature and gain adoption.

But if Deno is so promising, does it mean the end of Node? It’s a valid question. They’re both coming from the same place and trying to solve similar problems. But I think that’s extremely unlikely, at least for the foreseeable future. Node is incredibly popular. It’s mature, it’s robust, and what it does, it does very well. Deno, as exciting as it is, is still largely untried in production.

Another thing that suggests that Node could be around for a long time to come is the Node Package Manager (npm). Ironic, since Deno does without the one thing that really helped Node gain such a widespread following in the first place. There are well over a million packages in the npm registry, and that’s a huge draw for developers who are looking for pre-built solutions to common problems so that they are not constantly reinventing the wheel. If Deno is looking to compete with that, then first, it’s got a long way to go. And second, there’s the issue of discoverability. Sure, it’s great that you can create modules and publish them anywhere, but how are developers going to know those modules exist without a Deno equivalent of npm? It will be interesting to see how that plays out.

In general, though, there are more similarities than differences between Deno and Node. As we start to write Deno code in the remaining chapters, try to keep the following differences between Node and Deno in mind:

  • You’ll be writing code in TypeScript rather than JavaScript. (You can write JavaScript if you prefer, but that’s not really the “Deno way.”)
  • You’ll be importing modules (standard library and third party) via a URL rather than downloading and installing them locally with a package manager.
  • You have built-in tools at your disposal to help you develop your application.
  • When you execute your application, you’ll need to ensure that it has the appropriate permissions, or it won’t run.

Enough background—let’s build some cool applications and learn by doing!


Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.