Deno is a new Typescript runtime built on top of Google’s V8 engine. It recently reached milestone 0.1, which is the first major milestone for this project. I have worked a bit on Deno, mostly on implementing the synchronous write functions and imports from http resources.

The runtime seeks to fix a number of issue brought forward by NodeJS. No more unwieldy package.json or constantly changing dependencies, you either save your file in your project or load them from http ressources, after which they are cached forever. Also, gyp is absent from this, the project is now built with GN and calls to native function is no longer done directly, but through flatbuffers message passing. It also comes with Typescript support out-of-the-box.

But I’m not here to teach you about the greatness of Deno, but rather to give you a quick tutorial to get started with the new runtime.

Installing

The binaries are available from Deno’s Github repository . You can alos read the Readme from the repo and try to compile it yourself. The build process is rather resource-hungry, so make sure you have at least 4GB of RAM and a good SSD, or you could spend hours waiting for the build to finish.

Running scripts

In deno, you can run either Javascript or Typescript files. You can run your code by executing Deno with the filename as argument. Look into the repo’s tests folder to see a number of example Deno scripts.

Console output

As with any language / framework, the first thing you can do is output a message to the console. You can simply write the following to print Hello World!

console.log("Hello world!")

Filesystem Access

Deno offers basic synchronous filesystem access with the readFileSync and writeFileSync functions. The readFileSync function takes the file’s path as argument (which can either by a path relative to the current working directory or an absoule path) and returns the file’s contents. Create a file with ‘Hello World’ in it then save it to the filesystem, and read it by the executing the following:

import { readFileSync } from "deno";

const data = readFileSync("hello.txt");
console.log(data);

The output should be the following

➜  deno git:(master) ✗ out/debug/deno scripts/hello.ts
Uint8Array { 0: 72, 1: 101, 2: 108, 3: 108, 4: 111, 5: 32, 6: 87, 7: 111, 8: 114, 9: 108, 10: 100 }

Yes, readFileSync returns the data as an array of unsigned 8-bit integers. If we want to convert this to a string for printing, we have to use a TextDecoder. The following code will generate the proper output.

import { readFileSync } from "deno";

const decoder = new TextDecoder("utf-8");
const data = readFileSync("hello.txt");
console.log(data);

Writing

Now that we read and decoded a text file, the next thing we can do is write to a file. The writeFileSync function is there to do exactly that. The function takes 2 arguments: The path to the file, and the content of the file as an Uint8Array. A third parameter, perm, meant to set the file permissions, is not implemented in 0.1 and will probably go in 0.2, we changing this parameter for now has no effect. To write text to a file, we’ll have to use a similar trick as for reading, by using a TextEncoder to convert our String to a Uint8Array.

import { writeFileSync } from "deno";

const encoder = new TextEncoder("utf-8");
const data = encoder.encode("Hello world\n");
writeFileSync("hello.txt", data);

You should see something like the following. This is because Deno disallows writes by default. You need to specifically allow it to write to the filesystem for writeFileSync to work.

➜  deno git:(master) ✗ out/debug/deno scripts/hello.ts
deno.PermissionDenied: allow_write is off.
    at maybeThrowError (deno/js/errors.ts:13:11)
    at Object.writeFileSync (deno/js/os.ts:150:5)
    at eval (file:///data/Documents/deno/scripts/hello.ts:5:1)
    at localDefine (deno/js/compiler.ts:305:7)
    at eval (/data/Documents/deno/scripts/hello.ts, <anonymous>)
    at DenoCompiler.eval [as _globalEval] (<anonymous>)
    at DenoCompiler.run (deno/js/compiler.ts:423:10)
    at denoMain (deno/js/main.ts:88:12)
    at deno_main.js:1:1

To run Deno with permissions to write to the filesystem, execute it with the –allow-write flag. Inspecting the content of the files shows us that the write has been successful.

➜  deno git:(master) ✗ out/debug/deno --allow-write scripts/hello.ts
➜  deno git:(master)cat hello.txt                                
Hello world

Importing files

As with any project, putting all your code in one file can be cumbersome. You want to be able to import code from other files. Thankfully, Deno supports loading files from the filesystem, and from HTTP ressources (no HTTPS yet, but this will hopefully come soon). Deno executes the file when imported. You can also make use of ES6 modules.

Local imports

Create a file called print.ts and input this code. This exports a single function called printHello we can call from our main script.

export function sayHello (subject: string) {
    console.log(`Hello ${subject}!`);
}

Write this code in your hello.ts script

import { sayHello } from "./print.ts";

sayHello("deno");
sayHello("world");
sayHello("readers");

You should see the following output.

➜  deno git:(master) ✗ out/debug/deno scripts/hello.ts
Hello deno!
Hello world!
Hello readers!

You can then organize them any way you want. Imports are always relative to the calling file’s directory, and not the current working directory.

HTTP imports

Files can be imported from HTTP, but not HTTPS. Deno has an open issue for supporting HTTPS imports, and this is definitely something we want to land as soon as possible. The following script demonstrates an HTTP import.

Be careful though, HTTP requests are insecure and subject to man-in-the-middle attack, so you are effectively running untrusted code. Run this script at your own risk. I would reccomend you run your own localhost-bound HTTP server instead and serve the file from there.

import "http://dpourdiplome.5gbfree.com/hello.ts";

The first time you run this script, you will get the following console output.

➜  deno git:(master) ✗ out/debug/deno scripts/hello.ts            
Downloading http://dpourdiplome.5gbfree.com/hello.ts
Hello deno!

Deno fetched the HTTP resource and executed it. The new time you run the script, you will see the following…

➜  deno git:(master) ✗ out/debug/deno scripts/hello.ts
Hello deno!

Deno did not fetch the script this time. It used the cached version of the file. To find this cached file, go to your home directory. It should be under .deno/deps/dpourdiplome.5gbfree.com/hello.ts. Deno fetches all HTTP request once and caches them forever. You need to pass the –reload flag to force it to redownload its remote imports, or delete the file from the cache and run the script again.

Fetch

Finally, Deno supports basic fetch requests, again only on HTTP. Not all of fetch has been implemented yet, but basic functionnality is there. Fetch returns a Promise. You can check the request HTTP status and get its body. The following code fetches the neverSSL page and prints it to the console.

fetch("http://neverssl.com/").then(async (response) => {
    console.log(await response.text());
});

HTTP imports and fetch will eventually depend on a permission, just like writeFileSync does. It is not implemented yet, but the flag will probably be –allow-net when it is done.

Timers

Finally, timers have support for setTimeout, clearTimeout, setInterval and clearInterval. Those work just like they do in Javascript. They run asynchronously, take a callback as its first argument and a delay as the second. After all timers are done, the program exits. Here is an example with a 3 second delay.

setTimeout(function() {
    console.log("Done");
}, 3000);

This program should output:

➜  deno git:(master) ✗ out/debug/deno scripts/hello.ts
Please wait 3 seconds...
Done

And here is an example script that counts down for 5 seconds and its output.

console.log("Please wait 3 seconds...");


var done = false;
var countdown = 5;

var intervalTimer = setInterval(function() {
    console.log(`Please wait, ${--countdown} remaining`);
    if(countdown === 0) {
        clearInterval(intervalTimer);
    }
}, 1000);
➜  deno git:(master) ✗ out/debug/deno scripts/hello.ts
Please wait, 4 remaining
Please wait, 3 remaining
Please wait, 2 remaining
Please wait, 1 remaining
Please wait, 0 remaining

Conclusion / Reflections

Deno has done a lot of progress since being first mentionned by Ryan Dahl back in June. One of the major things that still hasn’t made it into Deno is SSL, and while writing this article I realized how hard it is to find resources on the web that can host files to be served only over HTTP. I had to create an account on a shady free web host, and I feel weird including this in my article. If someone wants to host the file and serve it, or point me to a file hosting or paste bin service that supports plain HTTP, I would gladly welcome it.