Hello deno: Building a minimal HTTP Server
TL;DR: Check the GitHub repo with the code I implemented here.
Everyone —really, everyone— is talking about Deno right now. As of today, the Javascript runtime has reached the 1.0.0 version (a.k.a. the stable one), there are still a lot of missing characteristics to work in a production environment the way we currently do.
And while their features might indicate there is a potential for a lot of real use cases (applications with Electron, React Native, lambda functions, etc.), I'm still a bit skeptical about the future of this runtime.
This, however, didn't stop me from getting my hands on the project. This is what I got.
Installing deno
If you go to their website, you'll get the simplest indications ever for downloading and installing the runtime. And I mean it: it's way simple.
For any Unix-based (including any Linux distro) system, run the following:
curl -fsSL https://deno.land/x/install/install.sh | sh
In the case of macOS, it's pretty simple:
brew install deno
Finally, for Windows:
choco install deno
First Run
The immediately following section in the landing is Getting Started. There, they'll introduce two examples: the classic console.log("Hello World")
and the HTTP-based Hello World.
You can find the console.log
approach, running the following console statement:
Then, there's the HTTP example.
I ran deno run server.js
. And it failed.
One of the newest features, and most announced of deno is that the runtime is sandboxed. That means you MUST explicitly enable the permissions via flags.
Once you add the flags, it runs correctly. Go to https://localhost:8000/
and you'll see the magic.
Batteries included, but…
Then, I asked myself: what if I output a JSON with a 🦕 emoji? With a small modification to the initial snippet, we end up with something like this.
However, when running in browser… it doesn't really look fine. Really, you'll get something similar to this:
{
"hello": "🦕"
}
And what's that, anyways? Well… it turns out it's the ASCII-encoded version of the emoji (😅😅).
To fix this, the response must indicate it's encoded in UTF-8. How do you get this? Just add the corresponding header.
Much fucking better ❤️🦕.
What about Routing?
One thing I noticed while debugging —notes on that later— this script is that, if I run the request on the browser, I'll get two requests in the server. The first request is for GET /
. The second one is for GET /favicon.ico
. This led me to plan a router with a 404 express-like fallback.
What's a Router? Basically, it's a code strategy that finds a handler given a request criteria. In this case, we route to method and uri.
How do we achieve that? We create an object that stores the handler functions for every method/uri, with a fallback if no method is found.
Then, replace the request.respond
inside the for loop with a call to #handler
.
async handleRequests () {
for await (const request of this.#httpServer) {
await this.handler(request);
}
}
Debugging
I wanted to check if the debugging worked the way it works with node. And indeed it did. However, there are slight changes:
- You have to specify the
host:port
inside the--inspect
while on node, you only have to indicate theport
, and the bindinghost
is optional. - If integrating with the Visual Studio Code debugger, you must add
[ "run", "--allow-net" ]
to theruntimeArguments
. Also, you must set a fixed debugging port, since you also need to indicate the host, and VSCode doesn't do that by default.
Wrapping up
The steps above mentioned are wrapped-up in the MiniServer: A Minimal HTTP Server for Deno library I just launched on GitHub. You are welcome to f0rk and improve it.
The usage for this library is pretty simple: just initialize the MiniServer
and add the handlers.
import { MiniServer } from 'https://raw.githubusercontent.com/pandres95/deno-http-server/master/server.js';
const server = new MiniServer({
port: 8000
});
server
.get('/', async request => {
request.json({
body: {
hello: 'deno 🦕'
}
});
})
.listen();