A whole new world with Deno

About 6 min reading time

Deno is a new runtime for Javascript. Before, NodeJS used to be the most popular way to run Javascript on the server. But no more! Deno has reached stable state (it's on version 1.17 at the time of writing). It runs Typescript natively, supports ES imports (why did I feel I had to specify that as a feature?), and is built with Rust (again! This isn't a feature, but it feels good that it's Rust somehow, doesn't it?).

Until now, I've just read the Deno manual and lurked in their standard library. Then I played around with a server + client app for a couple of days, and after that I converted this site to use lume, which is a static site generator based on Deno.

Here are some of my personal favourites in Deno, with some love for lume as an appendix.

Web APIs we know and love

Deno includes a ton of niceties that just sparks joy for the hardest of the hardcore NodeJS developers out there. fetch just works (we've internalised that one needs to npm install some external library for HTTP requests in NodeJS). Hell, even doing the very browser-y alert and confirm in your Deno based CLI script will work on the command line to show a message and prompt for yes/no, respectively. Opinions might differ whether this is good or bad, but personally I appreciate not having to pull in a lib or visit StackOverflow each time I need user input.

For APIs where a web standard already exists, like fetch for HTTP requests, Deno uses these rather than inventing a new proprietary API.

The above line from "The Runtime" section of the Manual is the red line throughout the Deno API.

That means, instead of doing

// Node
import request from 'some-random-npm-request-lib';

const response = await request.get('https://deno.land');

you'd do

// Deno/browsers
const response = await fetch('https://deno.land');

In Deno, you can use a ton of browser-y APIs, such as location, local/sessionStorage, and more.

Another example: you can use the same CSS based styling for console calls as you'd do in the browser:

// Deno/browsers
console.log(
    '%cHi there. %cSome background?',
    'color: red',
    'background-color: gray'
);

No need for terminal escape codes or pulling in yet another library.

I hope it's clear where I'm getting at: there are things included in Deno that you just know already because you've been writing web code for years. It lowers the iteration speed when writing code, as well as lowers the overhead when reading it, when you don't have to refer to third party dependencies or in-house modules all the time. To me, this feels much better when writing code in Deno.

The use of third party dependencies have been a debate in Javascript land during the past couple of months, due to … incidents around security and feelings of the module authors themselves. Surely the best dependency is no dependency, right? Surely a ton of npm modules can be deprecated if only NodeJS (or Javascript itself) had a better standard library?

I hope that we in the future look at "old" code and say: "Oh, did you pull in a dependency for that?".

Update, 2022-02-08: the Deno authors have written a neat blog post detailing all web platform APIs implemented in Deno, with examples. You'll get a very fuzzy feeling when reading the list.

Typescript as a first class citizen

I was in tears (tears of joy, I assure you) when I first read about Typescript support in Deno when the latter was introduced years ago. No need for a build step for .ts and .tsx files, just deno run and off you go. I just works.

One neat API is Deno.emit. With it, you can programmatically compile and bundle Typescript code to Javascript.

I've got not much more to say about this than "Finally".

Testing

Let's play a game. How many test runners for Javascript code can you think of?

Okay, tough question, as the correct answer probably is: "The number is approaching Infinity".

How many test runners do you need? One. There's one built-in in Deno:

Deno.test('My test', () => {
    // test things
});

Name the file something.test.ts and run it with:

deno test something.test.ts

I love this. Standardised, one way of doing things, minimal. No arguing in the team about which is the best test runner this month.

The testing module from the Deno std lib includes a couple of assertions – you probably don't need more than those. (I also see it exports tools for benchmarking your code as well, iihhh!)

Distributed dependency management

…with no package.json! In Deno, you import external, remote, dependencies with URLs. Again, just like we've done in the browser since the 90s (<script src="http://cdn.example.com/script.js> amirite). When Deno first runs your code, it'll fetch the remote script and cache it locally for the next runs. Don't be scared: Deno do support reading and writing a lockfile with options to deno run.

This is how it looks like:

import dependency from 'https://somesite.com/mod.ts';

(By convention, modules should have a mod.ts which is the entrypoint for consumers. Note that there's no concept of some silly index.js file in Deno land: that's one thing the Node creator Ryan wanted to get rid of when he created Deno.)

This is actually fairly liberating once you've sweated out the feelings of chaos in your codebase. Remember, this is all source code! You can centralise all these third party imports to a single file and re-export them. In fact, that's what they recommend in the Deno Manual:

// deps.ts (naming convention!)
export * as assert from 'https://deno.land/[email protected]/testing/asserts.ts';

"Sooo how do I get my fav npm hosted lib into my Deno code then?", you ask.

  1. Check deno.land/x if the module is hosted there. There are a lot of Deno specific modules.
  2. Import from any of the services below.

Services reading from npm:

Skypack and esm.sh will actually bundle code from npm into modern ES module syntax. Both of them are Deno friendly.

The beauty with these is that if one of them is failing, or if you're unhappy with the service, you can just switch out the URL imports in your deps.ts.

Tip: with Skypack and esm.sh (from what I know) you get Typescript types too. esm.sh will do this automatically, and with Skypack you can append a ?dts query parameter to the imported URL. See their respective config query parameters: it's super cool what they can do these days.

One thing that tripped me up was that it's not always easy to find a suitable entrypoint file when importing via URLs. Deno friendly modules has a mod.ts in the repo somewhere, but often you have to hunt down a file yourself.

Import maps

I've glossed over the technology that is Import Maps. In my own words, I'd say it's a file where you specify from where the source code's import statements should look for the code to be imported. In a way, it's emulating npm's way of looking in node_modules, but this is even more powerful.

If you were annoyed over importing "raw" URLs in the section above, this will save you!

Import map

{
    "imports": {
        "react": "https://esm.sh/[email protected]"
    }
}

Source code

import React from 'react';

Remember to specify --import-map for deno run! Nothing in Deno is implicit: it won't pick up the import map by itself.

Summary

My overall impression of Deno that while it's new technology, it's quite mature and well documented. And if you've got a hairy question, you can pop into their Discord server and ask there. The std modules are very modern in the way they're written (compared to Node, which just got Promise based APIs…). It's actually fun to write Javascript code again. I'd attribute that to the decrease of "Javascript fatigue" in Deno, since there are so many built-in modules you'd previously assume you'd need to get from a third party.

When (hehe) you install Deno, be sure to do deno help in a terminal to see the range of nice sub commands. deno types and deno doc are favourites of mine. deno compile is also cool if you're writing a script which should be run as a self contained executable, suitable for distribution.

Appendix: lume

lume is a static site generator, built with Deno. I read through the documentation and jumped around in the source code for a while before I fell in love and ported johanbrook.com to it.

Lume is a joy to work with. The built-in functionality and plugins fill 95% of my use cases – very little custom code needed. And if I don't understand the documentation, reading the source code is no problem.

It feels easier to use than other generators I've tried (Jekyll, Middleman, Metalsmith, Eleventy) but yet very powerful. During the porting my code I was not too annoyed during the process: something that you easily get when trying a brand new site generator. The fact that its written in Typescript is so nice, as you get static typing and don't have to guess what objects and properties to read.

Try it out!