Software Consulting Services

Modern Javascript Landscape

Tags: Technologies
Share

Table of contents

javascript

 

The javascript landscape finds itself in a perfect storm where if you take a look at all of the actors involved it is certainly encouraging to be a JS oriented developer. Given frontend solutions naturally involve browsers, mobile (though JS is not necessarily a first choice there) and other devices, you would think it is always the case that it is encouraging. I am referring specifically to the growing list of competitors and alternatives to Node. 

 

The javascript world has always been a competitive environment. You have always been able to choose from a wide variety of frameworks and libraries for UI concerns and in the near future this will change for other paradigms as well. The focus of this article is to bring attention to some of the newer players – Deno and Bun – and how Node is trying to keep up, which is all a big win if you are still looking for ways to leverage JS expertise into other areas without needing to sacrifice for transition time to other technology stacks.

 

First Impressions

 

We will have a non-FE point of view and focus on the following factors: developer experience, security, performance, typescript support and dependencies.

 

Node 24

 

The current Node release is version 24 and it was released this year in May. In terms of DevEx, Node hasn't changed much. If you have kept your stack updated then you might've noticed that npm is faster and node as a whole has been considering a wide array of security concerns, from the permission model (explicit permissions to system APIs) to stuff like npm audit which help manage the hard task of keeping your dependencies up to date and not open to vulnerabilities. In terms of performance, just updating Node to the latest current version should land you some noticeable performance bumps, thanks to V8 engine updates and also core standard library changes. 

 

We might also be reaching a time when we no longer need a separate cli tool to run typescript like ts-node or tsx, since Node 23 the experimental flag --experimental-strip-types allows you to ignore types and run ts directly. While it’s not direct typescript support we can treat typescript as just a typehint and checking framework along with a tsconfig file, without that stopping us from running the code or needing to transpile it first. Node 24 now also ships with its own test runner so that you can avoid requiring external testing frameworks.

Deno

 

Deno is one of the modern javascript runtimes, its aim is to offer a complete solution. Deno v1.0 was released in May 2020. Like Node it is also based in the V8 engine but it ships with everything you can think of: dependency manager, typescript support (not just type stripping but it actually understands it), data storage, key security features, limited interoperability with existing "node projects" and a whole set of tooling: REPL, built-in formatter and linter, test runner, debugger and compile js or ts into executables (although this is considered an unstable feature). 

 

As for performance, Deno should offer an edge when compared to Node, we will prove this in the following section. Deno was co-created by Ryan Dahl which was also the case for Node and its development is run by the Deno Company. Deno already boasts in its portfolio some known organizations: Netlify, Slack, Supabase and Bank of America.

Bun

 

Bun (v1, released in September 2023) sells itself as an "all in one javascript runtime and toolkit", its stated design objectives are: speed, elegant APIs (highly optimized apis, so faster) and cohesive developer experience, meaning you have everything you need to work. On the surface Bun and Deno appear to be the same thing but they aren't. Deno clearly is very concerned with security and overall being a modern JS runtime, fixing what it sees as flaws in Node, it even avoids the dreaded node_modules dir. Bun on the other hand is a completely different approach where speed and being a drop-in replacement for Node are the main concerns. 

 

When using Bun for the first time, it is pleasantly surprising how easy it is to get started if you are just probing for a possible project migration or starting a new project while speed and performance certainly being significantly much better than Node. Among Bun's users we can find Anthropic, Midjourney and X. Its typescript support is similar to Node's, it runs it by stripping types but you still rely on your IDE or typescript to do the type-checking. Bun also uses a different engine, JavascriptCore (webkit and safari) instead of V8.

Performance Comparisons

 

There are many benchmark tests out there that compare Node, Deno and Bun. We decided to make our own to see how it would compare to the other sources out there. The following results are based on an express + node/deno/bun implementation where we use 2 endpoints POST /forms which simulates payload processing and writing to a database table forms and GET /forms which fetches all of the records available in forms. This is the test script for node:

 

// server.ts

 

import express from 'express';

import { DatabaseSync } from 'node:sqlite';

import { faker } from '@faker-js/faker';

 

const db = new DatabaseSync('express.db');

db.exec('DROP TABLE IF EXISTS forms');

db.exec(`

  CREATE TABLE IF NOT EXISTS forms (

    id INTEGER PRIMARY KEY AUTOINCREMENT,

    value_one TEXT,

    value_two TEXT,

    value_three TEXT

  );

`

);

 

interface PostForm {

  value_one: string;

  value_two: string;

  value_three: string;

};

 

const insertForm = (values: PostForm) => {

  const { value_one, value_two, value_three } = values;

  db.prepare('INSERT INTO forms (value_one, value_two, value_three) VALUES (?, ?, ?);')

  .run(value_one, value_two, value_three);

}

 

const getForms = () => {

  return db.prepare('SELECT * FROM forms;')

  .all()

}

 

const server = express();

 

server.get('/', (_, res) => {

  res.send({ ok: true });

});

 

server.post('/forms', (_, res) => {

  // simulate a payload

  const values = { value_one: faker.word.preposition(), value_two: faker.word.adjective(), value_three: faker.word.noun() };

  insertForm(values);

  res.status(201).send({ created: true });

});

 

server.get('/forms', (_, res) => {

  res.send({ results: getForms() })

});

 

server.listen(3000);

console.log('Server is listening on port 3000');

 

In the case of Deno and Bun the script looks very similar but the way in which we install and import dependencies is different. In the case of Deno I had to include a @ts-types annotation // @ts-types="npm:@types/express@5.0.3" while running deno as deno run --allow-net --allow-env --allow-read --allow-write server.ts which is expected of Deno's security model. Dependency installs are similar to npm but there are a few workarounds to use node modules like deno install npm:express. In the case of Bun it was more of a familiar path: bun install and bun server. Finally, we ran tests using apache benchmark and running 10000 requests at a max concurrency of 1000.

 

Results

Time taken for tests (Lower is better)

 

“POST /forms”

Node 24

Deno

Bun

4.176s

 3.967s

3.662s

 

“GET /forms”

Node 24

Deno

Bun

62.280s

94.469s

26.703s

Complete Requests (Higher is better)

 

“POST /forms”

Node 24

Deno

Bun

10000

10000

10000

 

“GET /forms”

Node 24

Deno

Bun

10000

10000

10000

 

Requests Per Second (Higher is better)

 

“POST /forms”

Node 24

Deno

Bun

2394.47 #/sec

2520.55 #/sec

2730.74 #/sec

 

“GET /forms”

Node 24

Deno

Bun

160.57 #/sec

105.86 #/sec

374.49 #/sec

 

 

Time Per Request (Lower is better)

 

“POST /forms”

Node 24

Deno

Bun

0.418 ms

 0.397 ms

0.366 ms

 

“GET /forms”

Node 24

Deno

Bun

6.228 ms

9.447 ms

2.670 ms

 

 

Longest Request (Lower is better)

 

“POST /forms”

Node 24

Deno

Bun

349

160

213

 

“GET /forms”

Node 24

Deno

Bun

25

29

8

Our tests consistently show Node lagging behind both Deno and Bun. Bun stays true to its design aspirations of being the fastest.

Recomendations

 

Over at Rootstack we are always looking to integrate modern and exciting technologies. Our recommendations will not be straight-forward as each case should carefully consider the circumstances and needs of each individual project. Common sense points to the following:

 

  • Node is still a safe bet, based on evidence of recent updates it will keep adapting to its competition and adopting some of their features. Competition is heating up and Node is catching up. No need to change your stack.
  • Deno seems like an option if previously you were repelled by the idea of a js server generally speaking. It certainly has a long-term plan, has support within cloud vendors and it already has a significant amount of production systems being powered by it. Security has a priority and any new dependency you add will make this clear as any new type of system api usage will instantly let you know your app has changed its setup.
  • Bun is the most recent but also best performing no doubt, could be an option if starting a new project.

 

With that out of the way, changing the js runtime and overall techstack is challenging. Deno poses a lot of hurdles and potentially complete rewrites and refactors depending on your project's use cases, while Bun is still very new, you can easily swap with node. If you are already comfortable with a js ecosystem and starting from scratch, Deno should strike a nice balance between productivity, performance and security. An argument for Bun could be made for greenfield projects and those which have a fully covered CICD ecosystem, replacing node for bun is very easy from there on; it's just a matter of measuring and using an existing yet robust test suite that would guarantee bun is fully compatible with your project. 

 

It is a good time to be working with javascript, competition is driving the best out of each player while the onboarding time being very minimal thanks to the standards each solution is keeping.