DEV Community

Cover image for From Node to Deno
Aral Roca
Aral Roca

Posted on • Edited on • Originally published at aralroca.com

From Node to Deno

Original article: https://aralroca.com/blog/from-node-to-deno

Last week I published an article about Deno, and how to create a Chat app with Deno and Preact. Since then, many doubts have arisen. Mostly of them are about how to do the same thing we did in Node, but with the new Deno ecosystem.

I've tried to collect some of the most used topics in Node, and looked for their alternative with Deno. First of all, I would like to make it clear that we can use many of the current Node.js modules. There is no need to look for an alternative for everything, as many modules are reusable. You can visit pika.dev to look for modules to use in Deno. That said, let's start with the list:

We will cover the following:

Electron

With Node.js we can create desktop applications using Electron. Electron uses Chromium as interface to run a web environment. But, can we use Electron with Deno? Are there alternatives?

Electron logo

Well, right now Electron is far from being able to be executed under Deno. We must look for alternatives. Since Deno is made with Rust, we can use web-view rust bindings to run Destkop application in Deno.

This way, we can use the native OS webview to run as many webviews as we want.

Repo: https://github.com/eliassjogreen/deno_webview

import { WebView } from "https://deno.land/x/webview/mod.ts";

const contentType = 'text/html'
const sharedOptions = {
  width: 400,
  height: 200,
  resizable: true,
  debug: true,
  frameless: false,
};

const webview1 = new WebView({
  title: "Multiple deno_webview example",
  url: `data:${contentType},
    <html>
    <body>
      <h1>1</h1>
    </body>
    </html>
    `,
  ...sharedOptions,
});

const webview2 = new WebView({
  title: "Multiple deno_webview example",
  url: `data:${contentType},
    <html>
    <body>
      <h1>2</h1>
    </body>
    </html>
    `,
  ...sharedOptions,
});

await Promise.all([webview1.run(), webview2.run()]);
Enter fullscreen mode Exit fullscreen mode

Deno desktop app

Forever / PM2

Forever and PM2 are CLI tools for ensuring that a given script runs continuously as a daemon. Unlike Forever, PM2 is more complete and also serves as load balancer. Both are very useful in Node, but can we use them in Deno?

Forever is intended for Node only, so using it is not feasible. On the other hand, with PM2 we can use an interpreter.

PM2 logo

➜ pm2 start app.ts --interpreter="deno" --interpreter-args="run --allow-net" 
Enter fullscreen mode Exit fullscreen mode

Express / Koa

Express and Koa are the best known Node frameworks. They're known for their robust routing system and their HTTP helpers (redirection, caching, etc). Can we use them in Deno? The answer is not... But there are some alternatives.



Express and Koa logo

Http (std lib)

Deno's own STD library already covers many of the needs provided by Express or Koa. https://deno.land/std/http/.

import { ServerRequest } from "https://deno.land/std/http/server.ts";
import { getCookies } from "https://deno.land/std/http/cookie.ts";

let request = new ServerRequest();
request.headers = new Headers();
request.headers.set("Cookie", "full=of; tasty=chocolate");

const cookies = getCookies(request);
console.log("cookies:", cookies);
Enter fullscreen mode Exit fullscreen mode

However, the way to declare routes is not very attractive. So let's look at some more alternatives.

Oak (Third party lib)

One of the most elegant solutions right now, very inspired by Koa. https://github.com/oakserver/oak

import { Application,  } from "https://deno.land/x/oak/mod.ts";

const app = new Application();

app.use((ctx) => {
  ctx.response.body = "Hello World!";
});

await app.listen({ port: 8000 });
Enter fullscreen mode Exit fullscreen mode

Abc (Third party lib)

Similar to Oak. https://deno.land/x/abc.

import { Application } from "https://deno.land/x/abc/mod.ts";

const app = new Application();

app.static("/static", "assets");

app.get("/hello", (c) => "Hello!")
  .start({ port: 8080 });
Enter fullscreen mode Exit fullscreen mode

Deno-express (Third party lib)

Maybe the most similar alternative to Express Framework. https://github.com/NMathar/deno-express.

import * as exp from "https://raw.githubusercontent.com/NMathar/deno-express/master/mod.ts";

const port = 3000;
const app = new exp.App();

app.use(exp.static_("./public"));
app.use(exp.bodyParser.json());

app.get("/api/todos", async (req, res) => {
  await res.json([{ name: "Buy some milk" }]);
});

const server = await app.listen(port);
console.log(`app listening on port ${server.port}`);
Enter fullscreen mode Exit fullscreen mode

MongoDB

MongoDB is a document database with a huge scability and flexibility. In the JavaScript ecosystem has been widely used, with many stacks like MEAN or MERN that use it. It's very popular.



MongoDB logo

So yes, we can use MongoDB with Deno. To do this, we can use this driver: https://github.com/manyuanrong/deno_mongo.

import { init, MongoClient } from "https://deno.land/x/mongo@v0.6.0/mod.ts";

// Initialize the plugin
await init();

const client = new MongoClient();
client.connectWithUri("mongodb://localhost:27017");

const db = client.database("test");
const users = db.collection("users");

// insert
const insertId = await users.insertOne({
  username: "user1",
  password: "pass1"
});

// findOne
const user1 = await users.findOne({ _id: insertId });

// find
const users = await users.find({ username: { $ne: null } });

// aggregation
const docs = await users.aggregation([
  { $match: { username: "many" } },
  { $group: { _id: "$username", total: { $sum: 1 } } }
]);

// updateOne
const { matchedCount, modifiedCount, upsertedId } = await users.updateOne(
  username: { $ne: null },
  { $set: { username: "USERNAME" } }
);

// deleteOne
const deleteCount = await users.deleteOne({ _id: insertId });
Enter fullscreen mode Exit fullscreen mode

PostgresSQL

PostgresSQL logo

Like MongoDB, there is also a driver for PostgresSQL.

import { Client } from "https://deno.land/x/postgres/mod.ts";

const client = new Client({
  user: "user",
  database: "test",
  hostname: "localhost",
  port: 5432
});
await client.connect();
const result = await client.query("SELECT * FROM people;");
console.log(result.rows);
await client.end();
Enter fullscreen mode Exit fullscreen mode

MySQL / MariaDB

MySQL and MariaDB logo

As with MongoDB and PostgresSQL, there is also a driver for MySQL / MariaDB.

import { Client } from "https://deno.land/x/mysql/mod.ts";

const client = await new Client().connect({
  hostname: "127.0.0.1",
  username: "root",
  db: "dbname",
  poolSize: 3, // connection limit
  password: "password",
});

let result = await client.execute(`INSERT INTO users(name) values(?)`, [
  "aralroca",
]);
console.log(result);
// { affectedRows: 1, lastInsertId: 1 }
Enter fullscreen mode Exit fullscreen mode

Redis

Redis logo

Redis, the best known database for caching, has also a driver for Deno.

import { connect } from "https://denopkg.com/keroxp/deno-redis/mod.ts";

const redis = await connect({
  hostname: "127.0.0.1",
  port: 6379
});
const ok = await redis.set("example", "this is an example");
const example = await redis.get("example");
Enter fullscreen mode Exit fullscreen mode

Nodemon

Nodemon logo

Nodemon is used in development environment to monitor any changes in your files, automatically restarting the server. This makes node development much more enjoyable, without having to manually stop and restart the server to see the applied changes. Can it be used in Deno?

Sorry, but you can't... but still, there is an alternative: Denon.

We can use Denon as we use deno run to execute scripts.

➜ denon server.ts
Enter fullscreen mode Exit fullscreen mode

Jest, Jasmine, Ava...

Jasmine, Jest, Ava, Mocha logos

In the Node.js ecosystem there are a lot of alternatives for test runners. However, there isn't one official way to test the Node.js code.

In Deno, there is an official way, you can use the testing std library.

import { assertStrictEq } from 'https://deno.land/std/testing/asserts.ts'

Deno.test('My first test', async () => {
  assertStrictEq(true, false)
})
Enter fullscreen mode Exit fullscreen mode

To run the tests:

➜  deno test
Enter fullscreen mode Exit fullscreen mode

Webpack, Parcel, Rollup...

Webpack, Parcel, Rollup logos

One of the strengths of Deno is that we can use ESmodules with TypeScript without the need for a bundler such as Webpack, Parcel or Rollup.

However, probably you wonder if given a tree of files, we can make a bundle to put everything in one file to run it on the web.

Well, it's possible, yes. We can do it with Deno's CLI. Thus, there's no need for a third-party bundler.

➜ deno bundle myLib.ts myLib.bundle.js
Enter fullscreen mode Exit fullscreen mode

Now it's ready to be loaded in the browser:

<script type="module">
  import * as myLib from "myLib.bundle.js";
</script>
Enter fullscreen mode Exit fullscreen mode

Prettier

Prettier logo

In the last few years Prettier has become quite well known within the JavaScript ecosystem because with it you don't have to worry about formatting the files.

And the truth is, it can still be used on Deno but it loses its meaning, because Deno has its own formatter.

You can format your files using this command:

➜  deno fmt
Enter fullscreen mode Exit fullscreen mode

NPM Scripts

Npm scripts logo

With Deno, the package.json no longer exists. One of the things I really miss are the scripts that were declared in the package.json.

A simple solution would be to use a makefile and execute it with make. However, if you miss the npm syntax, there is an npm-style script runner for Deno:

You can define a file with your scripts:

# scripts.yaml
scripts:
  start: deno run --allow-net server.ts
  test: deno test --allow-net server_test.ts
Enter fullscreen mode Exit fullscreen mode

Execute with:

➜  vr run <SCRIPT>
Enter fullscreen mode Exit fullscreen mode

Another alternative is denox, very similar to Velociraptor.

Nvm

Version semantics

Nvm is a CLI to manage multiple active Node versions, to easy upgrade or downgrade versions depending on your projects.

A nvm equivalent in Deno is dvm.

➜  dvm use 1.0.0
Enter fullscreen mode Exit fullscreen mode

Npx

Npx in recent years has become very popular to execute npm packages without having to install them. Now many projects won't exist within npm because Deno is a separate ecosystem. So, how can we execute Deno modules without having to install them with deno install https://url-of-module.ts?

In the same way that we run our project, instead of a file we put the URL of the module:

➜  deno run https://deno.land/std/examples/welcome.ts
Enter fullscreen mode Exit fullscreen mode

As you can see, not only we have to remember the name of the module, but the whole URL, which makes it a little more difficult to use. On the other hand it gives a lot more flexibility as we can run any file, not just what's specified as a binary in the package.json like npx.

Run on a Docker

Docker logo

To run Deno inside a Docker, we can create this Dockerfile:

FROM hayd/alpine-deno:1.0.0

EXPOSE 1993  # Port.

WORKDIR /app

USER deno

COPY deps.ts .
RUN deno cache deps.ts # Cache the deps

ADD . .
RUN deno cache main.ts # main entrypoint.

CMD ["--allow-net", "main.ts"]
Enter fullscreen mode Exit fullscreen mode

To build + run it:

➜  docker build -t app . && docker run -it --init -p 1993:1993 app
Enter fullscreen mode Exit fullscreen mode

Repo: https://github.com/hayd/deno-docker

Run as a lambda

Lambda symbol

To use Deno as a lambda, there is a module in Deno STD library. https://deno.land/x/lambda.

import {
  APIGatewayProxyEvent,
  APIGatewayProxyResult,
  Context
} from "https://deno.land/x/lambda/mod.ts";

export async function handler(
  event: APIGatewayProxyEvent,
  context: Context
): Promise<APIGatewayProxyResult> {
  return {
    body: `Welcome to deno ${Deno.version.deno} 🦕`,
    headers: { "content-type": "text/html;charset=utf8" },
    statusCode: 200
  };
}
Enter fullscreen mode Exit fullscreen mode

Interesting references:

Conclusion

I'm sure I forgot some Node topics and their Deno alternative, let me know if there's anything I missed that you'd like me to explain. I hope this article helps you break the ice with Deno.

To explore all libraries you can use with Deno:

Top comments (35)

Collapse
 
yuripredborskiy profile image
Yuri Predborskiy

I'm afraid I'm missing something:
Deno doesn't have npm, right? So if I want a third party library, I have to go find it, grab the url, download it, and add it manually? Or use a URL, and if URL changes, I have to do the same thing again from the start?

I just don't see much point in Deno at this point, it takes one of the biggest advantages of Node, throws it out the window and declares itself the winner... and I just don't get it. From what I've read the author is proud with the fact deno has smaller and more predictable latency (from receiving request to answering request) but... handles 2/3 of node's requests per second, so we lose 1/3 of the performance for a bit of latency, and that is, like, one of the biggest advantages.

As I said, I just don't get it. If anyone can explain what is Deno supposed to replace or what problem it wants to solve, I'd be very grateful. Thanks in advance!

Collapse
 
dels07 profile image
Deli Soetiawan

Pika.dev, jspm, I believed someone will create something like npm later on, remember node didn't have npm from day one, like golang go mod, php composer, python pip, etc they are invented later on (even by third party developer)

Collapse
 
maskedman99 profile image
Rohit Prasad

Anything that gets rid of the bloated node_modules folder is a win for me.

Collapse
 
yuripredborskiy profile image
Yuri Predborskiy • Edited

I'm not a fan of all the bloat either, but it doesn't seem like we can change much, other than pulling all that code directly into our project and bloating the project files instead.

And then again, deno gets rid of bloated node modules folder how? By creating deno crates or something? My guess is it will be just as bloated very soon. It is a side effect from the ecosystem that relies on updateable plug-ins which use other plug-ins.

Thread Thread
 
quantumfillet profile image
QuantumFillet

Where would the bloat come from? Managing dependencies is as easy as creating a single typescript file with all the imports and Deno does the rest. No need to deal with node_modules. Here is a github issue discussing your grievances

Thread Thread
 
yuripredborskiy profile image
Yuri Predborskiy

This talks a lot about all the issues I have with deno way. Sadly, the discussion is simply closed without an answer.

Thread Thread
 
quantumfillet profile image
QuantumFillet

How about this reply

P.S. I'm not here to convert you to Deno xD, but if you dislike the idea of learning another technology in the already overwhelming web dev environment then I totally get where you are coming from.

Thread Thread
 
yuripredborskiy profile image
Yuri Predborskiy

It's actually CTCI + leetcode that take up all of my time. Including weekends.
I plan to devote some time to hobby projects down the line (including getting to know typescript, which seems to blend well with Deno), but it is not very high on my priority list at the moment.

Thread Thread
 
quantumfillet profile image
QuantumFillet

CTCI... oh boy.

I also have rather limited time atm, but if you want create something down the line with deno, let me know.

Collapse
 
quantumfillet profile image
QuantumFillet • Edited

So if I want a third party library, I have to go find it, grab the url, download it, and add it manually?

The download step is not required and I don't see how this is much different than the npm way of doing it... please eloborate.

Well if you prefer the npm and package.json way of managing modules, then yes Node will be more convenient for you - for now.

The main reason that the developers of Deno have given for the use of an import system via urls is "Furthermore the mechanism for linking to external libraries is fundamentally centralized through the NPM repository, which is not inline with the ideals of the web." (quoted from the v1 blogpost, I highly suggest the 15min read if you are struggling to see the point of deno). So essentially they are trying to avoid the whole central-source-of-trust-by-a-company thing that happened out of necessity in Node, because it was so hard to manage packages and are trying to make importing packages as easy as including javascript files in a webpage (This also means you don't have to manually install packages). After all, if you read the blogpost, the ambitions of deno are to be runtime environment like a web browser.

In my opinion the way Deno manages dependencies is also simpler and more elegant than Node, because if we compare the workflows:
Node: npm init to create project -> search for a package -> npm install it -> accidently push node_modules ^^
Deno: search for a package -> import via url -> done

But that is simply my opinion :)

Collapse
 
yuripredborskiy profile image
Yuri Predborskiy

I've read the blog post, I just disagree with this approach. When you have a centralized repository, you can have a more or less reliable source of information, like number of downloads and a place to search for modules. When every module is individual, without a centralized place to find them all, finding a module you want turns into an adventure with pirates, treasure hunts and fool's gold. Thanks but no thanks, in a corporate environment I want stuff that just works, period. I want to know popular repository that does exactly what I want, the way I want it to, I want it to be supported and updated and bring minimum extra dependencies. How can I know if any of this is true for a random url on the web without a centralized repository?

I prefer everything centralized which is shared in background for higher reliability and fault tolerance. In my personal opinion, npm is harder to compromise than random URL. But I may be wrong about it.

As for workflow: I initialize far too few repositories per day to make it matter. Once it is initialized, install work flow is easier for node - npm install, done. Most of the time the app is conteinerized and npm makes work flow simple. Making import statement from a fixed name is, IMO, also easier - you know for sure you are importing a validated package you installed through npm, a source you trust (I do, at least). Importing from url begs the question of audit, update (was the content replaced with a new version with breaking changes? Do I have to find the up to date version myself and manually update the link?).

This all seems counter-productive to me. Maybe deno is just not for me?

Thread Thread
 
quantumfillet profile image
QuantumFillet

Then I would just wait for a third party module auditer/provider like npm to arise (You will most likely still simply import via a url from a trusted domain of theirs, though).

I mean, if you are trying to get things done, then yes Node is probably better for you, because you already know it. At the moment Deno is still a young technology and doesn't have the 10 years of testing behind it's name. As the devs said in the v1 blogpost

For some applications Deno may be a good choice today, for others not yet.

I would at least give it a try in a hobby project, though, but I assume you have already done that... anyway the choice of technology is yours :)

Thread Thread
 
yuripredborskiy profile image
Yuri Predborskiy

I plan to try it together with typescript, they seem like a good match. I'm sure I won't be needing a lot of libraries when just starting out with TS.

Collapse
 
imichael profile image
✨iMichael✨ • Edited

Thanks for writing this up. Do you know if there a way to define a common host name for imports? From an aesthetic POV, this looks jarring to my eyes.

import { Client } from "https://deno.land/x/mysql/mod.ts";

It would be nice to do this instead

import { Client } from "mysql/mod";

and let some manifest or configuration flag handle the rest.

Collapse
 
aralroca profile image
Aral Roca

Yes! Deno supports import_map.json

References:
deno.land/manual/linking_to_extern...
wicg.github.io/import-maps/

Collapse
 
dels07 profile image
Deli Soetiawan

And there's might be a tool to manage this similar to npm in future

Collapse
 
max profile image
Maxime Lafarie

Nice post tho! 😉

Collapse
 
dels07 profile image
Deli Soetiawan

Like node will not replace php in past or more like rust will not replace c++ (in some area)?
I bet we will replace node when we (or big company) know what benefits and problem deno can solve, right now we don't see many benefits except for sandbox compared to node.

Collapse
 
wilsonuponsea profile image
Aoibhe Wilson 🇨🇭

What happens if one of your deps loses their URL or moves things around without warning? Is there any sort of central repository or cache?
I think I‘ve seen that the import map should be used to avoid having to update an import in a bunch of files so I suppose it’s not horrible to have to fix. Just seems like a risk.

Collapse
 
dels07 profile image
Deli Soetiawan

Deno will cache your deps in first run so it's available offline, similar to golang did before go mod

Collapse
 
uzitech profile image
Tony Brix • Edited

Is there anything preventing a left-pad situation?

Collapse
 
muuvmuuv profile image
Marvin Heilemann

Great article!!! I really think Deno will change the JS world forever and it was about the time to refactor NodeJS. I hate that we have yarn, npm etc (big fan of pnpm btw.) and everything is centralized and controlled by a company (npm inc.). This really explains some migration very good! I would love to see more like this in future and how e.g. Node libs are included and that Deno should change our thinking of building Node applications. Especially because it has some adoption from Go/Rust world and is TypeScript by default.

PS: add github.com/alosaur/alosaur/ to the framework section, it is a pretty solid alternative to nestjs.

Collapse
 
matthewadams profile image
Matthew Adams

You say "To use Deno as a lambda, there is a module in Deno STD library. deno.land/x/lambda."

FYI, deno.land/x is NOT the standard Deno library. In fact, there was noise on the #dev deno.land Discord channel about getting rid of deno.land/x because they're against centralization.

Collapse
 
matthewadams profile image
Matthew Adams
Collapse
 
craigmorten profile image
Craig Morten

Amazing article! One of the challenges with Deno atm is module discovery - it can be frustrating trying to find if an equivalent module exists!

One to consider for the list - I’ve ported Express over to Deno and Typescript in a framework called Opine github.com/asos-craigmorten/opine. It’s a port so honours the original internals and API (except where Deno deviates from Node)

Collapse
 
alvarezgarcia profile image
Alvarez García

Excellent compilation!.
I am starting with deno, specifically focusing on the very lightweight webview implementation (and in fact a nice way to learn internals)

Just one little thing in the webview section for the macOS users, just today the error when using directly an HTML string was fixed, wrapping the string with encodeURIComponent()

Collapse
 
yanai101 profile image
yanai

thanks 😊

Collapse
 
dels07 profile image
Deli Soetiawan

How about husky?