CHAPTER 2
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:
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
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
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:
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:

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
Windows cmd shell:
Code Listing 22
set DENO_DIR="C:\myproject#deno_modules" |
Windows PowerShell:
Code Listing 23
Deno will cache modules in that directory when your program executes, and you can check it into your version control system.
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:
We’ll be using several modules from both sources in the practical examples in subsequent chapters.
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
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.
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
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 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:
You can see all the tools available by executing:
Code Listing 28
Use deno help <tool> to learn how to use a specific tool. For example, the formatter:
Code Listing 29
$ deno help fmt |
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:
Enough background—let’s build some cool applications and learn by doing!