Deno: The next step in Node.js
Siddharth
Posted on August 4, 2021
Deno, introduced by Ryan Dahl, the creator of
Node during JSConf 2018 has been growing into a major alternative to Node.js. Deno is similar to Node.js – you write your scripts in JavaScript and run them – but Deno get's more powerful once you use it. It has first class TypeScript support, simplifies modules, is more secure, and bridges the gap between browsers and Node, and much more.
Node
Released in 2009, Node took over really quickly. Even though there was initially some skepticism about Node, support from the community was unrivalled.
Today, Node is one of the most popular tools used for backend development.
Enter Deno
Fun fact: Deno is just node reversed. no + de = node, de + no = deno.
Even though Node was great, there are many design mistake in it. You can check out the talk by Ryan Dahl to learn more, but here's a few:
- Node didn't stick with promises. Node had added them way back in 2009, but removed them almost a year later in 2010.
- Node wasn't secure enough. Any node program has access to system calls, http requests, filesystem calls. Your linter shouldn't have complete access to your computer and network.
- more...
Essentially, Node was focused on IO. Modules were an afterthought. To fix all this, Ryan introduced Deno.
Deno is secure by design
Suppose you want to run a lint script. If you were using node, you would just do this:
~$ node linter.js
But in Deno, you do this:
~$ deno run --allow-read linter.js
There's a couple of things to note here. First is the run
subcommand. Deno has a bunch of other tools, which we'll get to later.
Next thing to note is the flag --allow-read
. It, along with a bunch of other flags are part of deno's security system. By default, when a script is run using deno run
, it can't use anything more than the console.
Now, more security is great, but nobody wants to be putting in a bunch of --allow
flags everytime you need to run stuff. Fortunately, deno provides an install
command which can "install" stuff. Installing as an creating a thin wrapper in a platform-specific directory (~/.deno/bin
on MacOS and Linux, not sure about Windows).
~$ deno install --allow-read linter.js
✅ Successfully installed linter
/Users/APPLE/.deno/bin/linter
~$ linter
linter running!
The file at .deno/bin/linter
is very simple:
#!/bin/sh
# generated by deno install
exec deno run --allow-read 'file:///Users/APPLE/Sites/Projects/deno-test/linter.js' "$@"
No package managers here
Deno uses ES Modules import syntax, which means that imports must be full or relative paths to files. And unlike Node.js, there's no deno_modules
(thank goodness!), and deno doesn't look anywhere special for modules.
// These work
+ import {lint} from './linter.js';
+ import {lint} from 'absolute/path/to/linter.js';
+ import {WebSocket} from "https://deno.land/std@0.103.0/ws/mod.ts";
// But these wont:
- import {lint} from './linter'; // Note the extension is missing
- import {WebSocket} from "ws"; // ws who?
You don't have to relearn (most of) JavaScript
Deno tries to use web platform APIs (like fetch
) instead of inventing a new API. These APIs generally follow the specifications and should match the implementation in Chrome and Firefox. Deno even uses web standards in it's own APIs, for example Deno's http
API uses the standard Request and response objects. Deno's even got window
Node.js goes the other way replacing stuff with it's own APIs, usually using callbacks, making us reach for modules. Deno gets to take advantage of all the evolution of JavaScript instead of having to build it all again. Also, it's easier to port stuff to the web if you use Deno (and vice versa).
TypeScript is a first class citizen here
Deno has built in support for TypeScript ! This isn't just used as an external modules or anything, no extra flags, not even a tsconfig.json
. There is even interoperability – import JS in TS, import TS in JS
Simpler distribution
Unlike Node, Deno is just a single binary. This makes installation and deployment a breeze. Deno can even compile programs to binaries, which is absolutely awesome! It can even cross compile!
A simple demo
Here's a simple cat
implementation in deno:
// mycat.ts
import { expandGlob } from "https://deno.land/std@0.102.0/fs/expand_glob.ts";
// no need to remove the path to deno, etc.
const files = Deno.args;
files.forEach(async file => {
for await (const fileExpansion of expandGlob(file)) {
const contents = await Deno.readTextFile(fileExpansion.path);
console.log(contents);
}
});
This script takes filenames as arguments and prints them to the console.
~$ deno run --allow-read mycat.ts cat.ts
// cat.ts
import { expandGlob } from "https://deno.land/std@0.102.0/fs/expand_glob.ts";
// no need to remove the path to deno, etc.
const files = Deno.args;
...
Note that you don't need to install or configure anything - Deno handles that for you.
Now, we can install
the script:
~$ deno install --allow-read mycat.ts
✅ Successfully installed mycat
/Users/APPLE/.deno/bin/mycat
~$
Summary
Deno is still new. It has a thriving community and a bunch of libraries (many node libraries have been ported to deno). But it's not as popular or as supported as node. But deno's ease of use and simplicity make it useful for writing everyday scripts, and it's url-based system of sharing modules makes distributing programs as easy as putting them on a GitHub repo or personal site.
Posted on August 4, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.