Book Image

Deno Web Development

By : Alexandre Portela dos Santos
Book Image

Deno Web Development

By: Alexandre Portela dos Santos

Overview of this book

Deno is a JavaScript and TypeScript runtime with secure defaults and a great developer experience. With Deno Web Development, you'll learn all about Deno's primitives, its principles, and how you can use them to build real-world applications. The book is divided into three main sections: an introduction to Deno, building an API from scratch, and testing and deploying a Deno application. The book starts by getting you up to speed with Deno's runtime and the reason why it was developed. You'll explore some of the concepts introduced by Node, why many of them transitioned into Deno, and why new features were introduced. After understanding Deno and why it was created, you will start to experiment with Deno, exploring the toolchain and writing simple scripts and CLI applications. As you progress to the second section, you will create a simple web application and then add more features to it. This application will evolve from a simple 'hello world' API to a web application connected to the database, with users, authentication, and a JavaScript client. In the third section, the book will take you through topics such as dependency management, configuration and testing, finishing with an application deployed in a cloud environment. By the end of this web development book, you will become comfortable with using Deno to create, maintain, and deploy secure and reliable web applications.
Table of Contents (15 chapters)
1
Section 1: Getting Familiar with Deno
5
Section 2: Building an Application
10
Section 3: Testing and Deploying

Architecture and technologies that support Deno

Architecture-wise, Deno took various topics into consideration such as security. Deno put much thought into establishing a clean and performant way of communicating with the underlying OS without leaking details to the JavaScript side. To enable that, Deno uses message-passing to communicate from inside the V8 to the Deno backend. The backend is the component written in Rust that interacts with the event loop and thus with the OS.

Deno has been made possible by four pieces of technology:

  • V8
  • TypeScript
  • Tokio (event loop)
  • Rust

It is the connection of all those four parts that make it possible to provide developers with a great experience and development speed while keeping the code safe and sandboxed. If you are not familiar with these pieces of technology, I'll leave a short definition:

V8 is a JavaScript engine developed by Google. It is written in C++ and runs across all major operating systems. It is also the engine behind Chrome, Node.js, and others.

TypeScript is a superset of JavaScript developed by Microsoft that adds optional static typing to the language and transpiles it to JavaScript.

Tokio is an asynchronous runtime for Rust that provides utilities to write network applications of any scale.

Rust is a server-side language designed by Mozilla focused on performance and safety.

Using Rust, a fast-growing language, to write Deno's core made it more approachable for developers than Node.js. Node.js' core was written in C++, which is not known for being exceptionally easy to deal with. With many pitfalls and with a not-so-good developer experience, C++ revealed itself as a small obstacle in the evolution of Node.js core.

Deno_core is shipped as a Rust crate (package). This connection with Rust is not a coincidence. Rust provides many features that facilitate this connection with JavaScript and adds capabilities to Deno itself. Asynchronous operations in Rust typically use Futures that map very well with JavaScript Promises. Rust is also an embeddable language, and that provides direct embedding capabilities to Deno. This added to Rust being one of the first languages to create a compiler for WebAssembly, made the Deno team choose it for its core.

Inspiration from POSIX systems

POSIX systems were of great inspiration to Deno. In one of his talks, Dahl even states that Deno handles some of its tasks "as an operating system".

The following table shows some of the standard terms from POSIX/Linux systems and how they map to Deno concepts:

Some of the concepts from the Linux world might be familiar to you. Let's take, for instance, processes. They represent an instance of a running program that might execute using one or multiple threads. Deno uses WebWorkers to do the same job inside the runtime.

In the second row, we have syscalls. If you aren't familiar with them, they are the way for programs to perform requests to the kernel. In Deno, these requests do not go directly to the kernel; instead, they go from the Rust core to the underlying operating system, but they work similarly. We'll have the opportunity to see this in the upcoming architecture diagram.

These are just a couple of examples you might recognize if you are familiar with Linux/POSIX systems.

We'll explain and use most of the aforementioned Deno concepts throughout the rest of this book.

Architecture

Deno's core was initially written in golang, but it later changed to Rust. This decision was made to get away from golang as it is a garbage-collected language. Its combination with V8's garbage collector could lead to problems in the future.

To understand how the underlying technologies interact with each other to form the Deno core, let's look at the following architecture diagram for it:

Figure 1.3 – Deno architecture

Figure 1.3 – Deno architecture

Deno uses message passing to communicate with the Rust backend. As a decision in regard to privilege isolation, Deno never exposes JavaScript object handles to Rust. All communication in and out of V8 uses Uint8Array instances.

For the event loop, Deno uses Tokio, a Rust thread pool. Tokio is responsible for handling I/O work and calling back the Rust backend, making it possible to handle all operations asynchronously. Operations (ops) is the name given to the messages that are passed back and forth between Rust and the event loop.

All the asynchronous messages dispatched from Deno's code into its core (written in Rust) return Promises back to Deno. To be more precise, asynchronous operations in Rust usually return Futures, which Deno maps to JavaScript Promises. Whenever these Futures are resolved, the JavaScript Promises are also resolved.

To enable communication from V8 to the Rust backend, Deno uses rusty_v8, a Rust crate created by the Deno team that provides V8 bindings to Rust.

Deno also includes the TypeScript compiler right inside V8. It uses V8 snapshots for startup time optimization. Snapshots are used for saving the JavaScript heap at a specific execution time and restoring it when needed.

Since it was first presented, Deno was subject to an iterative, evolutionary process. If you are curious about how much it changed, you can look at one of the initial roadmap documents written back in 2018 by Ryan Dahl (https://github.com/ry/deno/blob/a836c493f30323e7b40e988140ed2603f0e3d10f/Roadmap.md).

Now, not only do we know what Deno is, but we also know what's happening behind the scenes. This knowledge will help us in the future when we're running and debugging our applications. The creators of Deno made many technological and architectural decisions to bring Deno to the state it is today. These decisions pushed the runtime forward and made sure Deno excels in several situations, some of which we'll later explore. However, to make it work well for some use cases, some trade-offs had to be made. Those trade-offs resulted in the limitations we'll examine next.