A deep dive into Deno and its comparison with Node.js

Introduction

In the hope that our children will surpass us, we try to protect them from our own mistakes and give them a better life. This sentiment mirrors Ryan Dahl's intentions when he developed Deno. Deno is often seen as the successor to Node. While many developers pit these runtimes against each other, it's an unfair comparison, as Deno was born out of Ryan's reflections on how he might have enhanced the module system, ensured stable legacy APIs, and bolstered security in Node.

It’s been three years since the emergence of Deno, and this JavaScript runtime has found its place in the development environment and the hearts of developers for its added features improvement. In this article, we will explore those features.

Prerequisite

I will strongly advise you to have a decent grasp of JavaScript and Node.js runtime environments, and module systems as this would be helpful to grasp the concepts and comparisons discussed in this article.

How to install Deno

Deno provides convenient scripts to download and install despite the variance of operating systems. Below are various installations available for different OS, these installations are obtained from Deno’s official Docs.

Using Shell (macOS and Linux):

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

Using PowerShell (Windows):

irm https://deno.land/install.ps1 | iex

Using Winget (Windows):

winget install --id DenoLand.Deno -e

Using Scoop (Windows):

scoop install deno

Using Chocolatey (Windows):

choco install deno

Using Homebrew (macOS):

brew install deno

Using MacPorts (macOS):

sudo port install deno

Using Nix (macOS and Linux):

nix-shell -p deno

To install Deno from Git directly you can use asdf (macOS and Linux):

asdf plugin-add deno https://github.com/asdf-community/asdf-deno.git 

asdf install deno latest 

# To install globally 
asdf global deno latest 

# To install locally (current project only) 

asdf local deno latest

Confirm installation

Once the command appropriate for your operating system has been executed, the Deno CLI binary will be installed on your computer. After the success message in the terminal, run deno and you should have this in your terminal:

C:\Users\Hp>deno
Deno 1.36.3
exit using ctrl+d, ctrl+c, or close()
REPL is running with all permissions allowed.
To specify permissions, run `deno repl` with allow flags.
>
// This confirms the installation by displaying the version in the terminal

To confirm the version that has been installed, run Deno --version. This command when executed should print the version type, followed by the Typescript version.

C:\Users\Hp>Deno --version
deno 1.36.4 (release, x86_64-pc-windows-msvc)
v8 11.6.189.12
typescript 5.1.6

To confirm the latest version, go to Deno’s site, and if it matches what has been downloaded here, begin your journey. If it doesn’t, you can simply run this command in your CLI:

C:\Users\Hp>deno upgrade
Looking up latest version
Found latest version 1.36.4
Downloading https://github.com/denoland/deno/releases/download/v1.36.4/deno-x86_64-pc-windows-msvc.zip
\[00:14\] [-----------------------------------------------------------------]

After installation, run Deno --version, and you should have the latest version. As of the time of writing this article, the latest version of Deno was version 1.36.4.

What is Deno JavaScript runtime

[@portabletext/react] Unknown block type "message", specify a component for it in the `components.types` prop

Deno was created by an American software engineer Ryan Dahl (born 1981), best known for creating the Node.js JavaScript runtime. Deno is all about making JavaScript on the server feel like JavaScript in the browser. It supports JavaScript, TypeScript, and WebAssembly. If you've bounced between Node and browser-based JavaScript, you've seen the API differences. Deno's goal is to smooth that out, giving you familiar browser-like APIs even on the server side.

Imagine Deno as a new kind of smartphone. Older phones (let's say, Node.js in this analogy) are functional and widely used, but you need to download lots of apps (third-party tools) to get all the features you want.

Deno, on the other hand, comes with many of those essential features built-in, like a better camera or a built-in flashlight, so you don't need to clutter it with extra apps, and these features are made available on every update. Plus, it has enhanced security features, like a top-notch fingerprint scanner, ensuring only approved actions are taken.

Deno has incredible features, but the five picked in this article to be discussed, are where it glitters the most.

Deno built-in TypeScript support

Typescript is the superset of JavaScript that compiles to plain JavaScript. If you are not familiar with Typescript, the image will give you an overview of what Typescript is and brings.

Let’s imagine JavaScript is like your favorite book. It's fun and engaging, and you can read it over and over. But sometimes, you might get a bit lost in the plot twists, ambiguous words, or the characters' motives, and as a little help, the author puts helpful notes where he feels his readers could get lost. Now, think of TypeScript as that same book, but with helpful notes in the margins.

Typescript specifies the data type being passed around a code, and when it picks an unmatched type it reports an error to the developer, This is so helpful and allows developers to write clean and easily debugged codes.

Unlike Node where you need a few configurations to get Typescript up and running, Deno offers built-in TypeScript support. This means you don't need any external tools or configurations to run TypeScript code. Let’s take a code example:

// greet function:
// Takes in a name.
// Returns a friendly greeting message using that name, saying "Hey there, [name]! It's great to see you."

function greet(name: string): string {
  return `Hey there, ${name}! It's great to see you. Hope you're having an awesome day!`;
}

// TwistGreeting function:
// Also takes in a name as an input.
// Flips the name backward (e.g., "KEN" becomes "NEK").
// Returns a message showing the name in this reversed form, saying "Just for fun, your name backward.

function twistGreeting(name: string): string {
  let twistedName = name.split('').reverse().join('');
  return `Just for fun, your name backwards is: ${twistedName}`;
}

console.log(greet("Bejamas"));
console.log(twistGreeting("Bejamas"));
console.log(greet("Marvel ken"));
console.log(twistGreeting("Marvel ken"));

The result:

That’s how Typescript is used in Deno. moving on we will be using only TypeScript.

Import from URL in Deno

One of the significant functions of Deno is its ability to import modules directly from a URL. It reduces the complexity of starting a new project or setting up a development environment. This also gives room for better performance. Once a module is downloaded, Deno caches it resulting in faster script execution. Importing from a URL seems to be safer as it reduces the risk of "dependency hole" where a module might have hidden or unexpected dependencies that could pose a security threat.

Let’s look at an example below;

import { serve } from "https://deno.land/std@0.76.0/http/server.ts";
const URL = serve({ port: 8000 });
console.log("http://localhost:8000/");
for await (const req of URL) {
  req.respond({ body: "Bejamas and Marvel " });
}

The serve function is imported directly from Deno's library in our code example above. You can specify the version of the module, ensuring that your application doesn't break due to updates. Run the code using deno run test.ts.

We have this rendered in our browser:

Security in Deno

By default, Deno runs in a sandbox, and when you execute the script, it doesn't have permission to the file system or network. Your computer is like your home. Inside are different rooms like the kitchen, bedrooms, and living room. Each room has its own purpose. Similarly, your computer has different functions, like accessing files and many more.

Imagine your rooms and kitchen do have doors but no keys. It gives a huge opportunity for unauthorized guests to overstep your boundaries and ruin your privacy. In a programming environment, some apps or scripts act like guests coming into your home and having the freedom to wander around your house, which is not very private.

With Deno, it's like your home suddenly has doors with locks and keys. If a guest who is likened to a “script” wants to enter a room that is likened to “using a function on your computer”, they need to ask for permission first. Now, Deno has flags for accessing different things, more like you give permissions for whatever a guest would want to do in your house.

They may ask "Hey, can I at least get bread from the fridge (read a file)?".

You, as the homeowner, get to decide - "Sure, but just the fridge. Stay out of my bedroom!"(using the flag). This way, the guest (in this analogy a script) sticks to only what they have permission for.

Now you have control over what each guest can or cannot do in your home. It's a more secure and transparent way to handle things, ensuring no sneaky guests are going through your personal stuff without you knowing.

Deno flags

Let’s take a look at the flags Deno supports and what they handle. We will need this information from the official docs, and then we can try out a few examples to wrap our hands around it.

Deno FlagsPermissions
–allow-env=It allows environment access for things like getting and setting environment variables.
–allow-sys=It allows access to APIs that provide information about the user’s operating system.
–allow-hrtimeIt allows high-resolution time measurement.
-A, –allow-allIt enables all security-sensitive functions. Use with caution.
–deny-write=It denies file system write access.
–Allow-read=It allows file system read access.
–deny-env=It denies environment access for things like getting and setting of environment variables.
–allow-write=It allows file system write access.

The table above contains information obtained from the official Docs.

--allow-env=

// example1.ts

console.log("Home Directory:", Deno.env.get("USERPROFILE"));

// For Windows

// Run this command using deno run --allow-env=USERPROFILE example1.ts

–Allow-read=

// example2.ts

// --allow-read Read file content and output

const text = await Deno.readTextFile("./text.txt");

console.log(text);

–allow-hrtime

The –allow-hrtime can be used in timing attacks and fingerprinting.

// example3.ts

const start = performance.now();

// Simulating some code that takes time
await new Promise(resolve => setTimeout(resolve, 1000)); // Delay for 1 second

const end = performance.now();
console.log(`Elapsed time: ${end - start} milliseconds`);

We have been able to look at a few examples of what permissions bring to the table.

Top-level await in Deno

In Node and other JavaScript environments, await is not usually welcome outside an async function. Here is an example of what I mean:

// Without top-level await, you would have to wrap code in an async function

async function main() {
    let response = await fetch("https://api.Bejamas.com");
    let fetchedData = await response.json();
    console.log(fetchedData);
}

main();

However, with Deno, await can be used outside, and this is how Deno handles this.

Deno introduces the concept of a Top-level await, which welcomes await to be used at the top-level of a module. It doesn’t prevent you from using Async but it doesn't necessitate it either.

Here is an example of how Deno uses it:

// Deno Top-Level await

let response = await fetch("https://api.Bejamas.com/data");

let fetchedData = await response.json();

console.log(fetchedData);

Deno standard library and third-party module management

In the standard library, we have all the libraries created and managed by the Deno core team. It’s a comprehensive library that covers a wide range of functionalities. If you open any package, you can see how to import and use any of the packages.

Deno advises that the standard library is not yet stable. Therefore, it is versioned differently than Deno. It is also strongly advised to always use imports with pinned versions of the standard library to avoid potentially causing compilation errors or unexpected behavior.

Instead of doing this:

// ❌ import the latest release, this should be avoided

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

Do this:

// ✅ imports from v0.201.0 of std, never changes

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

The codes above were obtained from Deno's official docs.

Understanding Node and its features

Node is a back-end Javascript open-source server environment that operates on the v8 engine. It can run on several operating systems. Node.js is often used by developers on the server side and in generating dynamic web pages. It confirms why JavaScript is popular and powerful as it can be used almost everywhere (back end and front end). A large number of companies use Node.js, these are the likes of Netflix, Uber, PayPal, Medium, Trello, and many more.

Let’s explore the key features of using Node.js.

Asynchronous and event-driven

Node.js has a non-blocking system, where it doesn’t wait for an API to return any data, before moving on to the next. When it asks for some data or performs an action, it doesn't sit idle waiting for the result. Instead, it continues to work on other tasks. Once the data is ready or the action is complete, Node.js is informed through a system of notifications. This approach allows Node.js to handle many operations at the same time, making it efficient.

Speed

Node runs on the JavaScript v8 engine. V8 is well known for its high-level performance and makes Node.js very fast in code execution.

Node Package Manager (NPM)

Node Package Manager provides a library of reusable modules and packages, making it much easier for developers to add more functionalities to their projects.

Node is single-threaded but highly scalable

Node.js works in a unique way in that it uses a single-threaded model and event looping. It means that although it processes tasks one at a time, it doesn't wait around. Instead, it keeps moving to the next task, making it efficient at handling many tasks at once, allowing developers to manage a lot of activities simultaneously. Node.js can serve many more requests with the same resources, making it a more efficient choice for many developers.

Node community support and licensing

Node.js is backed by a great community that always tries to make it better, so much gratitude to this dedicated community, as we get to see frequent updates, new modules, and quick solutions to certain issues. It is also important to note that Node.js is released under the MIT license.

Comparing Deno and Node.js

These runtimes will be compared on certain criteria which are:

  • Runtime architecture and performance
  • Package management and module ecosystem
  • Security features
  • ES Modules
  • Community and developer support
  • Browser API
  • Typescript
  • Built-in tool

Runtime architecture and performance

Deno and Node were built on the V8 JavaScript engine and are designed for high performance, especially in asynchronous I/O operations. Deno was developed using Rust, whereas Node.js was developed using c/c++ and this contributes to their great performance.

Package management and module ecosystem

In Node.js, npm is used to install packages and manage a project's dependencies. These packages are normally stored in Node modules, and there is a package.json file used to track and specify which packages.

On the other hand, Deno doesn't take a totally different approach as it also supports npm. However, instead of totally relying on NMP, Deno allows you to import packages directly from URLs. When you import a package, it is cached in your hard drive. It means that if you need to use the same package in another part of your project or a different project, you don't have to download it again. Deno will use the cached copy.

Security and sandboxing features

Deno places a strong emphasis on security. When you run a program in Deno, it's confined to a secure environment, which limits its access to the file system, network, environment variables, and execution of other scripts. Accessing these resources requires permissions. It means that when you execute a script in Deno, you need to provide specific flags to grant it permissions for these operations, as we have earlier discussed.

On the other hand, Node.js provides scripts with default access to the file system and network. While this offers ease of development, it can introduce a lot of potential security challenges.

ES Modules

Deno embraces ES modules, allowing you to use imports similar to how you would in vanilla JavaScript or React. It brings two main benefits over the Node.js traditional require system. First, whereas require loads resources in a synchronous manner, ES modules use asynchronous imports, leading to better performance. Second, with ES modules, you can selectively import just the parts of a package you need, which is more efficient and saves memory.

Community and developer support

Deno is gaining popularity with a rising number of GitHub stars and contributors. People like its security features, built-in tools, and fresh take on JavaScript runtime. Meanwhile, Node.js has been there for a decade with a huge community and a lot of libraries. While Node.js has a well-established ecosystem, Deno is trying to catch up.

Browser API

In Deno, you can use the browser APIs, like fetch, without installing any extra packages. In Node, you'd typically install something like the node-fetch package for this. With Deno, you also get native access to window objects, making your code cleaner with fewer package dependencies.

TypeScript

Deno has TypeScript built-in. Just save your files with a .ts extension and you're able to use TypeScript. No extras, as we have earlier discussed. In Node.js, despite you being able to use TypeScript, you'd need to install it and set up a TS config. It's a bit more stressful.

Built-in tools

Deno comes with built-in tools like a code formatter (deno fmt), a linter (deno lint), a test runner (deno test), and a language server for your editor, making development smoother without needing extra tools. In contrast, Node.js developers often turn to external tools like Prettier for these tasks.

Conclusion

Whether to choose Deno over Node.js, or vice versa, is a decision I'll leave in your hands after going through this article. The key is to select the one that best fits your project's requirements. If you're after a well-established JavaScript runtime with vast solutions across the web, Node might be the way to go. However, if you're familiar with TypeScript and prioritize security, Deno could be your pick. Currently, Deno's main challenge is its still-growing developer community. Thank you so much for sticking with this piece. Keep on coding!