DEV Community

Carlos Oliva Pascual
Carlos Oliva Pascual

Posted on • Originally published at stacknotice.com

Bun vs Deno 2 vs Node.js 22: The Complete 2026 Comparison

The JavaScript runtime landscape changed dramatically in the last two years. Bun hit 1.2 with significantly improved Node.js compatibility. Deno 2 launched with native npm support, killing the biggest objection to adoption. Node.js 22 LTS improved startup performance and added native TypeScript stripping.

In 2026, there are three serious options. This guide covers everything that actually matters.


TL;DR Comparison

Node.js 22 LTS Bun 1.2 Deno 2
HTTP req/s ~85k ~210k ~120k
Startup time ~50ms ~6ms ~20ms
TypeScript Strip only Native Native + type check
npm compat ✅ Full ✅ Full ✅ Full (via npm:)
Built-in bundler
Built-in linter/formatter
Best for Enterprise, legacy Edge, scripts, monorepos Security-first, new projects

Node.js 22 LTS: The Safe Choice

Node.js 22 became LTS in October 2024. If you're running a large existing codebase, nothing changes for you — and that's the point.

Native TypeScript support:

# Node.js 22.6+
node --experimental-strip-types server.ts
Enter fullscreen mode Exit fullscreen mode

Strips type annotations before execution. No type checking — just faster startup without a build step for simple scripts.

Improved node:test module:

import { test, describe } from 'node:test'
import assert from 'node:assert/strict'

describe('User service', () => {
  test('creates user with hashed password', async () => {
    const user = await createUser({ email: 'test@example.com', password: 'secret' })
    assert.notEqual(user.passwordHash, 'secret')
    assert.ok(user.passwordHash.startsWith('$2b$'))
  })
})
Enter fullscreen mode Exit fullscreen mode

Many teams are dropping Jest/Vitest for new projects. No configuration, no install.

When to use Node.js:

  • Existing large codebase — lowest migration risk
  • Enterprise environment — best tooling support, best hiring pool
  • Libraries with native bindings — widest compatibility

Bun 1.2: The Speed Demon

Built from scratch in Zig with JavaScriptCore instead of V8. The result is significantly faster startup and I/O.

Performance numbers:

# Startup time
time bun run script.ts   # ~6ms
time node script.ts      # ~50ms

# Package install (large Next.js project)
bun install   # ~1.2s
npm install   # ~18s
pnpm install  # ~8s
Enter fullscreen mode Exit fullscreen mode

HTTP throughput (hello world):

  • Bun: ~210k req/s
  • Deno: ~120k req/s
  • Node.js: ~85k req/s

Bun's built-in bundler:

await Bun.build({
  entrypoints: ['./src/index.ts'],
  outdir: './dist',
  target: 'node',
  minify: true,
})
Enter fullscreen mode Exit fullscreen mode

Node.js compatibility in Bun 1.2 — the big improvement over earlier versions:

  • Full worker_threads support
  • Complete node:crypto implementation
  • node:child_process (spawn, exec, fork)

Most packages that work with Node.js now work with Bun. Native addons (.node files) are the main exception.

Single binary compilation:

bun build --compile ./src/cli.ts --outfile my-cli
./my-cli --help  # Starts in <5ms, no Node.js required
Enter fullscreen mode Exit fullscreen mode

When to use Bun:

  • Lambda/Edge functions — cold start matters
  • CLI tools — user sees the startup latency
  • Monorepo workspace — bun install speed across 10+ packages
  • New greenfield projects

Deno 2: The Security-First Runtime

Created by Ryan Dahl (Node.js's creator) to fix the mistakes of Node. Deno 2 added npm compatibility that made Deno 1 impractical.

The security model — explicit permissions:

deno run --allow-net --allow-read=./data server.ts
Enter fullscreen mode Exit fullscreen mode
// deno.json
{
  "tasks": {
    "start": "deno run --allow-net --allow-env=DATABASE_URL,PORT src/main.ts"
  }
}
Enter fullscreen mode Exit fullscreen mode

In Node.js or Bun, any dependency you install has full filesystem and network access by default. In Deno, a malicious dependency can only do what you've explicitly permitted.

npm compatibility in Deno 2:

import express from 'npm:express'
import { z } from 'npm:zod'
import Stripe from 'npm:stripe'

// JSR — the TypeScript-first registry
import { Hono } from 'jsr:@hono/hono'
Enter fullscreen mode Exit fullscreen mode

No node_modules. Dependencies cached globally. First run downloads, subsequent runs are instant.

Built-in tooling (the biggest DX advantage):

deno fmt        # Formatter
deno lint       # Linter
deno check src/main.ts  # Type checker
deno test       # Test runner
deno doc        # Documentation generator
deno task start # Task runner
Enter fullscreen mode Exit fullscreen mode

One tool, no configuration files to maintain, no version mismatches.

TypeScript in Deno 2:

deno run --check server.ts  # Full type checking
deno run server.ts          # Strip only (faster)
Enter fullscreen mode Exit fullscreen mode

Unlike Node's --experimental-strip-types or Bun, Deno can run full type checking without a separate tsc step.

JSR — the TypeScript-first registry:

import { Hono } from 'jsr:@hono/hono'
import { assertEquals } from 'jsr:@std/assert'
Enter fullscreen mode Exit fullscreen mode

Published as TypeScript source — no compiled JS, no @types packages. Works on Deno, Node.js, and Bun.

When to use Deno 2:

  • Security-sensitive applications
  • New projects where tooling simplicity matters
  • TypeScript-first teams
  • Library authors (JSR is excellent for publishing)

Real-World Scenarios

REST API with database

// Hono — works on all three runtimes
import { Hono } from 'hono'
const app = new Hono()

app.get('/users/:id', async (c) => {
  const user = await db.query.users.findFirst({
    where: eq(users.id, c.req.param('id'))
  })
  return c.json(user)
})

// Node.js: node server.ts
// Bun: bun run server.ts
// Deno: deno run --allow-net --allow-env server.ts
Enter fullscreen mode Exit fullscreen mode

All three work. Pick based on team familiarity and deployment target.

CLI tool → Bun wins

6ms vs 50ms startup is significant when the user runs your tool on every file save.

Existing Next.js app → Node.js or Bun

# Bun as drop-in — zero code changes needed
bun install
bun run dev
Enter fullscreen mode Exit fullscreen mode

Most Next.js apps "just work" with Bun.

Monorepo → Bun wins on install speed

2 seconds vs 45 seconds across 15 packages adds up in CI.


TypeScript Support Comparison

Node.js 22 Bun 1.2 Deno 2
Type stripping
Type checking during run ✅ (--check)
tsconfig support Requires tsc
Paths/aliases Requires bundler

Deployment Compatibility

Platform Node.js Bun Deno
Vercel
Railway
Fly.io
AWS Lambda ✅ (unofficial)
Cloudflare Workers
Docker

Migration Guide

Node.js → Bun (lowest friction)

  1. curl -fsSL https://bun.sh/install | bash
  2. Replace npm install with bun install
  3. Replace node src/index.ts with bun src/index.ts
  4. Run your tests: bun test (Jest-compatible API)
  5. Check for native addon dependencies

Most projects work without code changes.

Node.js → Deno 2 (medium friction)

// deno.json
{
  "imports": {
    "express": "npm:express",
    "zod": "npm:zod",
    "@/": "./src/"
  },
  "tasks": {
    "dev": "deno run --allow-net --allow-env --allow-read --watch src/main.ts"
  }
}
Enter fullscreen mode Exit fullscreen mode

Main migration work:

  • Convert npm imports to npm: specifiers
  • Add explicit permission flags
  • Replace __dirname with import.meta.dirname

Recommendation

  • Staying on Node.js? Upgrade to 22 LTS, adopt --experimental-strip-types and node:test. Zero migration risk.

  • Starting something new? Try Bun first. Drop-in compatibility means low risk, speed improvements are real.

  • Security is first-class? Deno 2. The permissions model eliminates whole categories of supply chain risk.

  • Building CLI tools or Lambda functions? Bun. The startup speed advantage is the biggest practical difference.

The era of "Node.js or nothing" is over. The real risk is not choosing wrong — it's not evaluating the options at all.


Full comparison with more detail at stacknotice.com/blog/bun-deno-nodejs-comparison-2026

Top comments (0)