6 things I just learned after implementing my first Deno web service
Tags: deno, microservices, node
Published: 2020-08-22 10:20:58

As I've shared yesterday about [my list of microservices](https://dev.to/ndaidong/would-you-like-to-play-with-microservices-1ahh), today I try to implement an [API Gateway with Deno](https://github.com/ndaidong/microservices/tree/main/api-gateway-deno). Because I've created [Node.js version](https://github.com/ndaidong/microservices/tree/main/api-gateway-node) early, I though that it should be very simple to migrate to Deno, but not really. It taken about 3 hours of  reading the documentation and some tutorials!

Here are some notes for reference later.

### 1. Server and HTTPOptions

Similar to Node.js, Deno provides [http](https://deno.land/std@0.66.0/http), a standard library to handle web server. I've played with it a little bit. Then I quickly found that there is a drop-in replacement for Express named [opine](https://deno.land/x/opine@0.21.2). 

So I switch to this lib. It works almost the same as Express, but incomplete, and the `listen` method is quite different.

With Express, to set `host` and `port`, we can just write:

```ts
app.listen(port, host, onServerReady);
```

But with Opine's `app` instance, the following ways didn't work:

```ts
app.listen(port, host, onServerReady);
app.listen(host, port, onServerReady);
app.listen({port, host}, onServerReady);
```

There is no docs/examples relating to this issue, so I have to look in the [source code](https://deno.land/x/opine@0.21.2/src/application.ts#L516) and see that they use `HTTPOptions` and `HTTPSOptions` from the standard [http/server](https://deno.land/std@0.65.0/http/server.ts#L314) library. They didn't use the property name `host` as [server.listen() in Node.js] (https://nodejs.org/api/net.html#net_server_listen_options_callback), but `hostname`. So, the correct way is:

```ts
app.listen({hostname: host, port}, onServerReady);
```


### 2. HTTP Proxy

As other API Gateway, I need to map some endpoints to the actual services behind the scene. In Node.js, I use [http-proxy-middleware](https://www.npmjs.com/package/http-proxy-middleware). In Deno, there is [opine-http-proxy](https://deno.land/x/opineHttpProxy@2.1.0).

I haven't dig into these libs enough yet. But the basic method I'm using shows a little bit of a difference. 

With express/http-proxy-middleware, if we write something like:

```js
app.use('/login', createProxyMiddleware('https://abc.com'));
```

Then, any request to `{API_GATEWAY_DOMAIN}/login` will be forwarded to `https://abc.com/login`. 

But, with opine/opine-http-proxy, we have to write exactly the path:

```ts
app.use('/login', createProxyMiddleware('https://abc.com/login'));
```

### 3. The flags

With Node.js, we simple run `node script.js` and everything should work well. But with Deno, `deno run script.ts` may not work as expected. Because, Deno does not grant any permission to running script by default. So, if the script needs to access to the network, it must be started with the flag `--allow-net`:

```
deno run --allow-net script.ts
```

If it also needs to load a file from hard disk, it must be started with the flag `--allow-read`:

```
deno run --allow-net --allow-read script.ts
```

There is also `--allow-write` to write files, `--allow-env` to get environment variables, `--allow-run` to run sub processes, and [more here](https://deno.land/manual/getting_started/command_line_interface).

However we can use `--allow-all` to enable all the permission so it would work as same as Node.js, but we shouldn't - that is the unique value by Deno.


### 4. `readJson` and `--unstable` flag

Because in my service there is always [a `service.json` file](https://github.com/ndaidong/microservices#servicejson-file) to define service settings, I need to parse JSON file.

Similar to Node.js, Deno provides a standard library called [fs](https://deno.land/std@0.66.0/fs) too. And I found that there is few familiar methods to deal with JSON content there.

However, the following does not work:

```json
import { readJson } from "https://deno.land/std/fs/mod.ts";
```

![](https://i.imgur.com/j6YbHXF.png)

As you see, it loads many unnecessary modules and requires to add `--unstable` to the command, for example:

```bash
deno run --allow-net --allow-read --unstable app.ts
```

In the case you don't like the unstable things, you can refer their [read_json.ts](https://deno.land/std@0.66.0/fs/read_json.ts) and write your own method, for example: 

```ts
const readJson = async (filePath: string) => {
  const content = await Deno.readTextFile(filePath);
  return JSON.parse(content);
};
```


### 5. Built-in logger

The [log](https://deno.land/std@0.66.0/log) library in Deno looks quite similar to Python [logging](https://docs.python.org/3.8/library/logging.html) module. Because I also work with Python so this is not very hard to get familiar with.

This lib supports file rotating, custom format and handlers. So we don't need to use any third party module as with  Node.js we must.



### 6. Which docker image for Deno should be chosen?

While Deno team didn't build any official docker image yet, I recommend to use [hayd/deno-docker](https://hub.docker.com/r/hayd/deno). Because it is updated regularly and there is [a discussion](https://github.com/denoland/deno/issues/3356) about making it official.


## Conclusion

Node.js is cool. Deno is cool too. There is no `package.json`, nor `node_modules`. That makes things look more simple and clean. The standard libraries and built-in tools are great. But this ecosystem will need more time to mature...