Node.js vs. Deno vs. Bun: JavaScript runtime comparison

SnykSec - Sep 6 '23 - - Dev Community

JavaScript runtimes help you build advanced, server-driven JavaScript projects that aren't dependent on the user's browser to run.

There are several choices of runtimes available, with the supremacy of the old stalwart Node.js being challenged by Deno and Bun. Deno is the latest project produced by the same developer who originally created Node.js, Ryan Dahl, back in 2009. Deno aims to improve its security via fine-grained access controls and offer more modern features such as native TypeScript support and better web compatibility.

Meanwhile, Bun is the latest upstart, offering blistering speed and significantly outperforming its rivals. However, it is still in beta and has some gaps to fill out to be fully production-ready (version 1.0 is currently planned for September 7th, 2023).

Let's get right to it and look at the different runtime.

Why choosing the right JavaScript runtime matters

JavaScript runtimes let you run application code outside the browser. This means you can deliver sites as hosted applications. Or you can choose to use JavaScript runtimes for general-purpose scripting.

The runtime you choose will have a significant impact on the performance of your applications, with request handling and database access speeds varying considerably. It also affects the ease of development and scalability.

Beyond the different features available and how they impact performance, developer experience is important as well. Your team's preferences, such as the experience they have with the runtimes and their willingness to experiment, will all factor into which runtime is right for you. Do you prefer stability or speed? Do you want native TypeScript support with a richer runtime or a more customizable environment? These are just a few examples of questions you might ask yourself when deciding on a runtime. Knowing what each runtime offers can help you make the most educated decision.

Introducing the JavaScript runtimes

Before you compare these runtimes based on their performance, stability, and security, let's give a basic overview of each:

Node.js

Node.js is the reigning champion of JavaScript runtimes and was ranked the #1 most popular web technology in 2023 by Stack Overflow developers. It was created by Ryan Dahl and launched in 2009. It's fair to say it revolutionized what you could do with JavaScript when it entered the market. With it, developers can create advanced backend-driven applications using JavaScript.

Today there is a vast ecosystem centered on Node.js, with resources and libraries galore. But as with any runtime or technology, there is always room for improvement. This is where Deno and Bun come in to add options in the JavaScript runtime landscape.

Deno

Deno is a Rust-based JavaScript runtime. Like Node.js, it was created and launched by Ryan Dahl with the hopes of improving upon what is provided in Node.js. You can see and hear more about Ryan’s motivation behind Deno in this recorded talk from JSConf EU.

One of its main focuses is to improve Node.js security. In Deno, file, network, and environment access must be explicitly enabled so that security issues that typically arise from these areas are less likely. It's also designed to better support JSX and TypeScript, as well as be more oriented toward web standards. To ease deployment, it ships applications as a single-contained executable.

Deno also has a tooling ecosystem around it to enable developers to jumpstart their projects. Fresh is a web framework built for Deno and Lume is their static site generator.

Bun

Bun is the latest runtime competing for your attention. Powered by Zig, its aim is to be an all-in-one runtime and toolkit with a focus on speed, bundling, testing, and compatibility with Node.js packages. One of its biggest draws is its performance which is demonstrably faster than both Node.js and Deno. This makes it a very attractive proposition if it can deliver on all of that.

Regarding its performance, the Bun maintainers provide an example benchmark running an HTTP handler that renders a server-side page with React. This resulted in Bun handling about 68,000 requests per second compared to about 29,000 and 14,000 for Deno and Node.js, respectively. That's quite a difference. Jarred Sumner regularly provides updates on the development of Bun along with recent benchmarks on Twitter so be sure to follow him to keep up to date with it all.

Bun also includes bundling and task-running features for JavaScript- and TypeScript-based projects. Similar to Deno, it ships single binaries and has Web API support built in. It also supports some Node.js libraries with npm compatibility.

Comparing JavaScript Runtimes

Now, let's take a more detailed look at the differences, focusing on performance, support and community, stability, security, and additional features.

Performance

Let’s get right to the point, Bun wins. We learned earlier about its performance capabilities regarding how many requests per second it can handle, which is quite impressive. It's a similar story when it comes to database operations. Average queries per second when loading the Northwind database for SQLite using Bun’s benchmark sample are as follows:

Runtime Average queries/second
Node.js 21.29
Deno 43.50
Bun 81.37

In another comparison between Node.js, Deno and Bun, Bun is the fastest to handle concurrent connections. Its requests per second are quite higher too. For instance, with 10 concurrent connections, Bun achieves 110,000 requests per second while Node.js achieves 60,000 and 67,000 for Deno. This trend continues with increased concurrent connections and how each runtime performs.

While there is some contention on the effectiveness of the scenarios used for the performance tests, Bun remains the winner. Node.js is last in all of the comparisons, doing particularly poorly when it comes to database speed. Deno and Node.js aren't too far apart generally, and Bun far surpasses both of them. While Node.js is behind in this regard, Yagiz Nizipli, is leading efforts to improve performance around several aspects within Node.js. For example improving URL parsing speeds by ~80-90%.

FIXME Unknown Tag - contentEmbedWidget
You can also run the Bun performance tests in your own environment to see how they perform in the following scenarios:

Bun's speed has been a major focus for its developers and uses JavaScriptCore, found in Safari. Whereas Deno and Node.js use the same V8 JavaScript engine found in Chrome. There are plans to improve Bun further by removing dead code in compiled binaries (it’s been partially implemented so far).

As well as running fast, Bun is also designed to start quickly — the aim being to remain performant as you spin multiple instances. That makes it a good choice for dynamically scaling applications. If you get a sudden traffic spike and need to create instances quickly, Bun is designed with this in mind to make your services available as fast as possible.

Support and community

All three projects are open source, but not all are supported entirely by the community. Node.js is backed by the OpenJS Foundation and is strictly community and volunteer-based. Deno and Bun are supported by for-profit organizations and VC-backed projects.

Node.js has an established ecosystem and vast community with online guidance available for every use case you can think of. In contrast, Deno and Bun are much newer, so you won't find as much support material. Still, there's no shortage of enthusiastic developers willing to share their knowledge. Also, Deno 1.28 introduced better compatibility with npm packages to make it easier to adopt for developers migrating from Node.js.

Here's a table showing how many questions are tagged for each runtime on Stack Overflow (as of September 2023):

Runtime Questions tagged
Node.js 466,762
Deno 917
Bun 52

As you can see, Node.js has significantly more hits making it more likely you’ll find an existing question related to one you have or that you’ll receive a relevant answer to it.


Every year there’s a developer survey called the State of JavaScript. In it, there’s a question around which runtimes participants use regularly for which there were nearly 30k respondents. The results from the most recent survey in 2022 also paint Node.js as the clear leader, with Deno trailing at about 5.3k votes and Bun getting about 1.2k votes. This is to be expected at this time and it will be interesting to see how these numbers change in the upcoming 2023 survey results. Maybe we’ll see some new trends for Deno and Bun.

The official Node.js docs include various guides, a large API reference, and information on getting started. There's also information available on its dependencies.

Deno's site includes a very detailed manual to help you get acquainted with the runtime and start using it in your projects. Its homepage puts access to installation and documentation at the forefront, so newcomers don't have to hunt around for help. There's also some information on the standard library that developers can put to use right out of the box. The third-party modules page is handy to know what’s available in the ecosystem. It contains over 6,000 modules (as of August 2023) with some example code.

Bun's home page links prominently to its Discord, docs, and GitHub pages. The docs have improved significantly since they were first made available. They now have a comprehensive dedicated site, packed with information covering various topics such as getting started, using the bundler and test runner as well as an API reference. It even now has guides that show how to accomplish common tasks with Bun.

Stability

As the established player, Node.js offers proven performance, powering 2.1 percent of the world's websites. It's a known quantity and a product that a large number of projects depend on. If you do run into an issue with it, chances are someone knows how to fix it.

Deno's version 1.0 came out in May 2020 and is considered the first stable release. However, adoption has been slow, perhaps because of no “user perceived” differentiating factor to win developers over en masse. Since its 1.0 release, it has made strides in improving the developer experience while maintaining stability throughout each release so that developers can upgrade with little to no impact. 

Bun is still in beta, version 0.7.3, at the time of writing, but is close to its 1.0 release which is expected in September 2023. As a result, its stability and coverage of Node.js core APIs has increased significantly since being released in beta and will likely continue as more folks in the community look to adopt it in their projects.

Security

Security can be a weak point when it comes to dependency management with npm and extends to general application security pitfalls when building on top of Node.js. Creating a more secure runtime is one of the driving factors in Deno's creation, which revolved around fine-grained access controls to sensitive APIs such as network requests, file system operations, and other core capabilities. Node.js isn’t far behind on this front. Node.js 20 introduced a permissions model allowing similar security aspects as those of Deno.

There are plenty of resources available to help you learn secure practices, such as [Snyk’s Node.js security best practices article and this OWASP cheat sheet. Making developers aware of common mistakes can help you build a secure Node.js application.

Deno closes some of the security concerns inherent in Node.js by requiring explicit permissions for certain actions within the running application. For instance, in order to enable your application to access reading from the file system you must initiate it with the --allow-read flag at startup. You can see a full list of these in their permissions documentation. You can also interact with this permission system at runtime which can enable you to request and revoke permissions programmatically. Be careful of its subprocesses when using the --allow-run flag, as the spawned subprocesses do not have the same security restrictions as the Deno process and can invalidate Deno's security sandbox, leading to privilege escalation.

Bun is still very new, and information on its security is hard to come by. Its relative immaturity means you should use it with caution and keep a keen eye on updates and announcements for security patches. There are plans to introduce security audits once it becomes more stable.

Speaking of keeping up with security updates, Snyk can simplify managing the security of your project from the open source dependencies it relies on to the code you write yourself. Snyk Open Source (SCA) helps you find, prioritize, and fix security vulnerabilities and license issues in your open source dependencies, while Snyk Code (SAST) helps you scan your source code in minutes — no build needed — and fix issues immediately.

Additional features

Node.js has recently introduced some features to bring it more in line with Deno's and Bun's capabilities. It now has a built-in test runner, and built-in TypeScript support is under active discussion.

Deno includes a dependency inspector and code formatter. Its ability to deploy to a single executable is also a plus. When setting up a server with Deno, the basic method described in its documentation involves pulling code from other places. The code doesn't look much more complicated than with Node.js, but it can feel a bit different as dependencies are loaded via URL.

Here's a cut-down example of the code:

import { serve } from "https://deno.land/std@0.198.0/http/server.ts";

const handler = async (_request: Request): Promise => {
 const resp = await fetch("https://api.github.com/users/denoland", {
 // The init object here has an headers object containing a
 // header that indicates what type of response we accept.
 // We're not specifying the method field since by default
 // fetch makes a GET request.
 headers: {
 accept: "application/json",
 },
 });

 return new Response(resp.body, {
 status: resp.status,
 headers: {
 "content-type": "application/json",
 },
 });
};

serve(handler);
Enter fullscreen mode Exit fullscreen mode

Additional features for Bun include a transpiler and package manager. As hinted at in the name, it also includes bundling features, giving you the functionality that would otherwise require another tool, such as Snowpack or rollup.js. It also has a dead code elimination feature through its JavaScript minifier.

If using Bun as a task runner, its speed can be a big advantage. It claims to take ~5 milliseconds to start vs. ~25 milliseconds for Node.js. It may not seem like a significant difference but when you combine this with several tasks you need to run over time it can be impactful to speeding up your development workflow.

Migrating from Node.js to Deno or Bun

Code written in pure JavaScript or TypeScript should work seamlessly in any runtime. However, if you've used specific features of Node.js, you may have a harder time migrating to other runtimes. Let's investigate this briefly.

Migrating to Deno from Node.js

The Node.js module compatibility had been a major issue with Deno migrations in the past. However, it’s now as simple as prefixing your import statements with node:. As for npm packages you can prefix those with npm: or you can create a deno.js file that describes import maps for Deno to resolve them.

If you’re building packages/libraries for the community to use you can take a look at Denoify. It’s a project that aims to change some of your files automatically when migrating and make it easier to maintain the project for both npm and deno.land/x.

Migrating to Bun from Node.js

Bun implements most of the Node-API functions. If your project is small or only uses common functions, you may be able to drop it straight into Bun and get started. With larger projects, the chances of running into issues are higher. That means big projects could run into challenges having to rewrite code.

It also has its own APIs. For example, Bun uses its own API to serve web files. Creating a secure web server is as easy as applying the following code which is adapted from Bun's documentation:

Bun.serve({
  fetch(req) {
    return new Response("Hello!!!");
  },
  tls: {
    key: Bun.file("./key.pem"),
    cert: Bun.file("./cert.pem"),
  }
});
Enter fullscreen mode Exit fullscreen mode

As you can see with migration to either Deno or Bun, using their native API’s means your code will diverge from that used in Node.js. This is something worth keeping in mind when converting an existing project but also when starting a new one as it could lead to difficulty turning back should you run into a showstopping challenge that wouldn’t be present in Node.js.

Which project is right for you?

You should consider all the earlier features when deciding which project to use. Your priorities will vary according to your use case and project needs.

Bun is clearly the winner on speed, but as it is still very new, there are risks to using it. Will it develop the stability of the other two? Perhaps. Either way, if you're a disrupter looking to make waves by outperforming the competition on speed, Bun offers that opportunity (in most scenarios).

The big advantage of Node.js is its maturity and the size of its ecosystem. You'll find plenty of developers who understand it. However, Deno and Bun have the appeal of being newer, which always excites developers.

Deno also has plenty of advantages over Node.js, with its feature set making for smoother development and making it easy to build complex projects at a high quality. It's secure, but while faster than Node.js, compared to Bun it’s a bit slower.

In general, Node.js is still the safest option, with a proven record of success. Deno has much to recommend, with its modern features making it a good choice for developers looking to build something new. And Bun is the tool of choice if you care the most about speed or just want to ride the bleeding edge of new technology.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .