Index
- Introduction
- How install
- Hello world
- Dependencies
- Simple API demo
- More about Deno
Introduction
Maybe the better way to introduce the Deno is explaining who is your creator and the javascript runtime environment created by him years ago, Ryan Dahl create the Node in 2009 was the first server-side javascript environment, that is before Node, javascript was just executed in browsers. The node made much success but in 2018 in the same conference where Ryan introduce the Node he presented 10 Things I Regret About Node.js where he pointed his regressions and introduce your new project the Deno.
“Deno is a simple, modern and secure runtime for JavaScript and TypeScript that uses V8 and is built in Rust.”
How Install
You can install the Deno in Windows, MacOS and Linux without very difficult, but the installation will depends of the operational system you use, soo, you can skip directly to installation in your SO, how I use macOS the rest of examples in this post will be executed in this SO.
Linux
In this operational system the one way to install then in official documentation is:
1curl -fsSL https://deno.land/x/install/install.sh | sh
MacOS
If you use MacOS, you can install with the same command above too, but if you use the package manager Brew, you can install with the command:
1brew install deno
Windows
Case you don’t use any package manager, you can install in Power Shell with command bellow:
1iwr https://deno.land/x/install/install.ps1 -useb | iex
Now in the case of package manager the installation is very similar, with chocolatey:
1choco install deno
And finally with scoop
1scoop install deno
After the installation to check if always is ok, you can verify the version of Deno, you should get a message like this:
1$ deno --version2deno 1.0.13v8 8.4.3004typescript 3.9.2
Hello world
We can get started with a simple server, this block of code bellow are in main page of Deno, in this few lines if youve contact with node, you to perceive some differences. But let’s run this code to see what happens.
deno run welcome.ts
1// welcome.ts2import { serve } from "https://deno.land/std@0.50.0/http/server.ts";3const s = serve({ port: 8000 });4console.log("http://localhost:8000/");5for await (const req of s) {6 req.respond({ body: "Hello World\n" });7}
Running this you will get an error:
1error: Uncaught PermissionDenied: network access to "0.0.0.0:8000",2run again with the --allow-net flag3 at unwrapResponse ($deno$/ops/dispatch_json.ts:43:11)4 at Object.sendSync ($deno$/ops/dispatch_json.ts:72:10)5 at Object.listen ($deno$/ops/net.ts:51:10)6 at listen ($deno$/net.ts:152:22)7 at serve (https://deno.land/std@0.50.0/http/server.ts:261:20)8 at file:///Users/jefferson.silva/Projects/test-deno/index.ts:2:11
This revel a very important part of Deno, be secure by default, anything is disabled unless you change it, it serve to File system, network or environment access, soo to run this demo server you need some command like this:
deno run --allow-net welcome.ts
.
Write every time this flags can be bussing, so to simplify I create a Makefile to some Deno commands. The fmt command will auto-formats TypeScript and JavaScript code.
1# Makefile23serve:4 deno run --allow-net welcome.ts56fmt:7 deno fmt
Dependencies
In Deno the third-party imports are called by URLs like javascript script in the browser, the download of this imports are made just at the first time, when you do it, this module was save in $HOME/Library/Caches/deno.
This imports is an exception of the network access, in example above where we don’t pass any flag, the Deno made the download with success and just after when he tries execute de server occurred the error, because the runtime has special access to download imports and cache then to disk.
The local imports is very similar to imports in Node, but in this case is required use the extension of files. I will create an another file just for example, and put in him the port constant.
1// constants.ts2export const port = 8000
To import this file in our welcome, we need pass the relative path of file along with your extension. With this, I put this variable in server configuration, and in the log about here the server are running.
1import { port } from "./constants.ts"23import { serve } from "https://deno.land/std@0.50.0/http/server.ts";4const s = serve({ port });5console.log(`http://localhost:${port}/`);6for await (const req of s) {7 req.respond({ body: "Hello World\n" });8}
Different of Node, the Deno has no node_modules, a folder inside project with all dependencies, or package.json, a file with a list of dependencies that the project use. How I comment above the dependencies is saved globally and the dependencies and your versions stayed just in each file when you import then.
Simple API demo
For explore a little more the Deno let’s go write a simple application using the Oak a middleware framework for Deno, the objetive is just see how we can configure variable environments, routers, controllers, etc… soo we will use a static data in a typescript file. To this example we will need this list of files:
- Makefile
- .env
- server.ts
- app.ts
- router.ts
- controller.ts
- db.ts
To start let’s create config files, I will create a Makefile to have the Deno commands more easily, are just two descriptive commands, one for format e another to run the server, and I configured the server to format the application every time before run. Note that I already pass to the Deno run the flags that are necessary to the application works well. verify if your Makefile are using tabs and not spaces.
If we use just Deno to run the application, after every change in code we need to stop the server and run it again, over time it start to get tiring, so I use denon this module update the server automatically for us. In the link has instructions to how install.
1# Makefile23serve: fmt4 denon run --allow-net --allow-read server.ts5fmt:6 deno fmt
And an environment file to put the port that our application will use.
1# .env23PORT=9000
In server file that was executed in our Makefile, we need to get the PORT variable that was put in our environment file, to do it was used the dotenv module, he exports a config function that returns all of our variables. And we need the app file too, where are configured the application.
1// server.ts23import { config } from "https://deno.land/x/dotenv/mod.ts";4import { app } from "./app.ts";56const { PORT } = config();78console.log(`Server listening on ${PORT} 🦕`);9await app.listen({ port: parseInt(PORT) });
Some configurations in app file is necessary too, we just need start the application using the oak framework and pass some middleware, the routers and the permission of methods.
1// app.ts23import { Application } from "https://deno.land/x/oak/mod.ts";45import { router } from "./router.ts";67export const app = new Application();89app.use(router.routes());10app.use(router.allowedMethods());
The router file is responsible to declare all routers of the application and say which HTTP methods this routers accept, in this example we will use just the get methods for keep it simple. The first route will return all resources, and the last just what match with the passed id.
1// router.ts23import { Router } from "https://deno.land/x/oak/mod.ts";45import { getBooks, getBook } from "./controller.ts";67export const router = new Router();89router10 .get("/book", getBooks)11 .get("/book/:id", getBook);
The routers are configured above but who handle with request is the controller, in here we put what the application will response back to who requisite this route.
1// controller.ts23import { books } from "./db.ts";4import { RouterContext } from "https://deno.land/x/oak/router.ts";56export const getBooks = async (context: RouterContext) => {7 context.response.body = Array.from(books.values());8};910export const getBook = async (context: RouterContext) => {11 if (context.params && context.params.id && books.has(context.params.id)) {12 context.response.body = books.get(context.params.id);13 }14};
And finally in our db file, we put a Map with some books data.
1// db.ts23interface Book {4 id: string;5 title: string;6 authors: string;7}89export const books = new Map<string, Book>();1011books.set("1", {12 id: "1",13 title: "Production-Ready Microservices",14 authors: "Susan J. Fowler",15});1617books.set("2", {18 id: "2",19 title: "Design Patterns: Elements of Reusable Object-Oriented Software",20 authors: "Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides",21});
Running the application with our Makefile executing the command make serve, you got it some like that:
1$ make serve2deno fmt3denon run --allow-net --allow-read server.ts4[denon] v2.0.25[denon] watching path(s): *.*6[denon] watching extensions: ts,js,json7[denon] starting `deno run --allow-net --allow-read server.ts`8Compile file:///Users/your-user/project-folder/server.ts9Server listening on 9000 🦕
To test our routes you can execute some cURL commands in your terminal, json_pp is just for return a response more prettier, the response will be something like this:
1$ curl http://localhost:9000/book | json_pp23[4 {5 "id" : "1",6 "title" : "Production-Ready Microservices",7 "authors" : "Susan J. Fowler"8 },9 {10 "id" : "2",11 "title" : "Design Patterns: Elements of Reusable Object-Oriented Software",12 "authors" : "Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides"13 }14]1516$ curl http://localhost:9000/book/1 | json_pp1718{19 "id" : "1",20 "title" : "Production-Ready Microservices",21 "authors" : "Susan J. Fowler"22}