Part of my Saturday morning routine is to explore a new technology I find interesting. It's limited to one hour so that I won't drift away and stay focused.
Usually it begins with a simple "Hello, World" to setup my environment, and continues on to discover some basics around the eco system.
If it's a new runtime or a programming language, I will try to setup something a bit more realistic than the hello-world
example - REST API most of the time will do the trick.
Hello, Deno!
Deno is a simple, modern and secure runtime for JavaScript, TypeScript, and WebAssembly that uses V8 and is built in Rust.
from Official website
It was created by Ryan Dahl who also authored Node.js
and while it's not suppose to be a replacement, it does try to fix some of Node.js
disadvantages.
Let's start! For mac users all you need to do is:
❯ brew install deno
❯ echo "console.log('Hello, Deno!')" > hello.ts
❯ deno run hello.ts
Check file:///Users/shikloshi/projects/deno/hello.ts
Hello, Deno!
This is a working Deno
application.
What about NPM and node_modules
?
Deno
was first introduced in order to fix a lot of the things going on with Node.js
eco system that Ryan Dahl was not a fan of.
Package management and npm
was always kind of controversial topics in Node.js
community but either way, we did learned a lot from this approach.
In Deno
there is no real package manager for now, and no node_modules
directory that holds all your project dependencies.
If you want to use Deno
third party libraries you can do it via Remote Imports.
⚠️ This is a very wide and interesting topic that I would probably invest time in understanding deeply in the future.
We will start using destjs
soon so let's import it to our main.ts
file and create our app:
import { createApp } from "https://deno.land/x/destjs@v0.1.2/mod.ts" // This is a remote import - NEAT
createApp({port: 8000});
For now - createApp
- simply initalize our app with our controllers and expose endpoints via oak
http server.
When running this, we can access our app via localhost:8000
.
⚠️ If you are not familiar with
NestJS
- I would say that the library expects files to be located in a specific directory tree (a type of convention over configuration). For instance: any exported class from controllers directory, with the compelling decorators, gets registered as a server endpoint.
Let's create our first application controller. Simply put, controller is where our HTTP endpoints are being defined and where we call any business logic related to this endpoint.
We will call our first controller controller/plant.controller.ts
and for now it will look like this:
import { Controller, Get } from "https://deno.land/x/destjs@v0.2.0/mod.ts" // 3
import { HttpContext } from "https://deno.land/x/destjs@v0.2.0/types.ts";
@Controller("/plants") // (1)
export default class PlantsController {
@Get("/") // (2)
getOne(context: HttpContext) {
console.log(context.state)
return { plant: "Monstera", beautiful: true }
}
}
When we call this controller endpoint http://localhost:8000/plants
with a HTTP GET request, we get in response a beautiful Monstera. Later on we will use a database to store our plants and do some operations on it.
Let's zoom in on a few things:
- (1)
@Controller('/plants')
decorator - For now, what we need to know about it is that it create a family of HTTP endpoints that is accessible in our application. Here we can see/plants
endpoint. - (2)
@Get('/')
decorator for fetching a plant with HTTP GET request to/plants
. - (3) Remote Import for our library.
This is a standart NestJS
way of minimzing a lot of the boilerplate we used to have with Express.js
.
Troubleshoot library issues
This is where it was suppose to run, but an error occurs:
❯ deno run --allow-net --allow-read ./main.ts
error: Uncaught (in promise) NotFound: No such file or directory (os error 2), readdir 'middlewares'
for await (const item of Deno.readDir(name)) {
^
at async Object.[Symbol.asyncIterator] (deno:runtime/js/30_fs.js:125:16)
at async readFolder (https://deno.land/x/destjs@v0.2.0/bootstrap/middlewares.ts:8:20)
at async initializeMiddlewares (https://deno.land/x/destjs@v0.2.0/bootstrap/middlewares.ts:27:3)
at async createApp (https://deno.land/x/destjs@v0.2.0/mod.ts:19:3)
Currently destjs
will fail if it didn't find middlewares
directory under our project root directory. I'm not sure this behavior is by design or a bug - But for now we will create it even if we are not going to write any middlewares.
❯ tree -L 2
.
├── controllers
│ └── plants.controller.ts
├── main.ts
└── middlewares
2 directories, 2 files
After creating this directory structure our command can execute succesfully:
❯ deno run --allow-net --allow-read ./main.ts
Middlewares initialized! 0ms
Check file:///Users/shikloshi/projects/deno/hello-deno/controllers/plants.controller.ts
Controllers initialized! 2106ms
DestJS application ready at port 8000
Test it using curl
Using curl
http client we are able to submit an http request and verify that we are indeed getting the http response we are expecting to see:
❯ curl localhost:8000/plants
{"plant":"Monstera","beautiful":true}
Permission flags
Deno
, unlike Node.js
, does not get allowed permissions by default to use host network and file system.
-
destjs
is going to read from disk at build time sodeno
will beed to get explicit permission to do so by adding--allow-read
. - When running the server, it will have to use host network features and get explicit permissions using
--allow-net
. I encourage you to explore permission flags in depth when getting to knowDeno
.
Whats next?
In the next few chapter of this post We will try to:
- Add persistence layer using
PostgreSQL
. - Authentication layer using
middlewares
. - We will also explore
openAPI
. - Enhance our API to do some really interesting things, like saving plants in DB and checking when it was last watered.
Further reading
If you liked this piece and want to deepen your understanding of some of the underlying concepts. Here are some links:
- Oak server - middleware framework for Deno's native HTTP server. 'destjs' use it under the hood.
-
NestJS - amazing framework on top of
Express.js
that is being "ported", so to speak, intoDeno
. It has great concepts and helps us write business logic on top of well designed construct. - Ryan Dahl first introducing
Deno
- Recommended!
Stay tuned.
Top comments (0)