Toolmingo
Guides14 min read

Bun vs Node.js vs Deno: Which JavaScript Runtime in 2025?

An in-depth comparison of Bun, Node.js, and Deno — covering performance, TypeScript support, npm compatibility, built-in APIs, security, and when to choose each runtime.

Node.js ruled server-side JavaScript for 15 years. Then Deno (2018) and Bun (2022) challenged the throne. Both promised to fix Node's biggest pain points — clunky TypeScript setup, security holes, and a bloated node_modules. In 2025, all three runtimes are production-ready. Here's the unbiased breakdown.

At a glance

Node.js Deno Bun
Created 2009 (Ryan Dahl) 2018 (Ryan Dahl) 2022 (Jarred Sumner)
Engine V8 V8 JavaScriptCore (WebKit)
Language C++ Rust Zig
TypeScript Manual (ts-node/tsx) Native, built-in Native, built-in
Package manager npm / yarn / pnpm deno add / jsr Built-in bun install
npm compatibility ✅ Native ✅ Deno 2.x ✅ Full
Security Unrestricted by default Permission-based (--allow-*) Unrestricted by default
Startup time ~50–80 ms ~30–50 ms ~7–12 ms
HTTP performance ~65k req/s ~110k req/s ~180k req/s
Stability ✅ Battle-tested (15 yrs) ✅ Stable (v2.x) ⚠️ Maturing (v1.x)
Best for Production APIs, ecosystem depth Security-critical apps, scripts Speed-first APIs, scripts, testing

What is Node.js?

Node.js (2009) introduced JavaScript on the server using Google's V8 engine and a non-blocking I/O model. It became the world's most popular runtime, powering everything from Discord to Netflix.

Node's strengths:

  • Largest ecosystem: 2M+ npm packages
  • 15 years of production hardening
  • Massive community and job market
  • Every hosting provider supports it

Node's weaknesses:

  • TypeScript requires extra setup (ts-node, tsx, or transpilation)
  • CommonJS vs ESM tension (both co-exist awkwardly)
  • No built-in security model — any code runs with full OS access
  • Slow npm install with deep node_modules trees
// Node.js: still needs setup for TypeScript
// npm install ts-node @types/node
const http = require('http');
const server = http.createServer((req, res) => {
  res.end('Hello from Node.js');
});
server.listen(3000);

What is Deno?

Deno was created by Ryan Dahl — the same person who built Node.js — as a direct response to his regrets about Node. Announced in 2018, Deno uses V8 but is written in Rust, with TypeScript built in from day one.

Deno's key innovations:

  • TypeScript support out of the box (no config needed)
  • Security model: code runs in a sandbox, explicit permissions required
  • URL-based imports (original) → Deno 2.x added full npm compatibility
  • Standard library maintained by the Deno team (JSR registry)
  • Web-standard APIs: fetch, Request, Response, URL without polyfills

Deno 2.x (2024) was a turning point: Full npm support, package.json support, and node_modules compatibility made Deno a viable drop-in for most Node projects.

// Deno: TypeScript runs natively, no config
// deno run --allow-net server.ts
Deno.serve({ port: 3000 }, (_req) => {
  return new Response("Hello from Deno");
});

Permission flags:

Flag Grants access to
--allow-net Network (can scope to specific domains)
--allow-read File system reads
--allow-write File system writes
--allow-env Environment variables
--allow-run Subprocess execution
--allow-all (-A) Everything (use sparingly)

What is Bun?

Bun (2022) is the newest entrant, built from scratch by Jarred Sumner using Zig and Apple's JavaScriptCore (JSC) engine instead of V8. The goal: be the fastest all-in-one JavaScript toolkit.

Bun ships as a single executable that replaces:

  • node (runtime)
  • npm / yarn (package manager)
  • ts-node / tsx (TypeScript runner)
  • jest (test runner)
  • esbuild (bundler)
// Bun: TypeScript runs natively, fastest startup
// bun run server.ts
const server = Bun.serve({
  port: 3000,
  fetch(req) {
    return new Response("Hello from Bun");
  },
});
console.log(`Running on http://localhost:${server.port}`);

Bun's key claims:

  • bun install is 10–25× faster than npm install
  • HTTP server throughput 2–3× faster than Node.js
  • Built-in test runner (Jest-compatible API)
  • Built-in SQLite via bun:sqlite
  • Near-complete Node.js API compatibility

Performance benchmarks

Tests run on equivalent hardware (AWS c6g.xlarge, 2025). All frameworks using minimal HTTP handler. Numbers are approximate — run your own benchmarks for your specific workload.

Benchmark Node.js 22 Deno 2.x Bun 1.x
HTTP req/s (simple) ~65,000 ~110,000 ~180,000
Cold start time ~60 ms ~40 ms ~8 ms
npm install (50 deps) ~30 s ~15 s ~3 s
TypeScript execution ~2 s (transpile) ~0.3 s ~0.05 s
SQLite read (10k rows) ~180 ms (better-sqlite3) ~220 ms ~60 ms (bun:sqlite)
JSON parse (10 MB) ~55 ms ~50 ms ~30 ms
Memory (idle) ~35 MB ~45 MB ~25 MB

Key takeaways:

  • Bun wins on raw speed in most micro-benchmarks
  • Deno leads Node.js on HTTP by ~70% (Rust I/O layer)
  • For CPU-bound work, engine choice (V8 vs JSC) matters more than the runtime wrapper
  • In real-world apps with database calls and I/O, differences shrink considerably

TypeScript support

Feature Node.js Deno Bun
TS support Via ts-node / tsx / tsc ✅ Built-in ✅ Built-in
tsconfig.json Required Optional Optional
Type checking at runtime No (ts-node check mode) No (uses esbuild strip) No (strips types)
Decorators (Stage 3) Via tsc
JSX/TSX Via transform
Setup effort High (install deps + config) Zero Zero

Note: Neither Deno nor Bun perform full type-checking at runtime — they strip types for speed. Use tsc --noEmit or deno check for type verification in CI.


Module system and npm compatibility

Feature Node.js Deno Bun
CommonJS ✅ (primary) ✅ (Deno 2.x)
ESM ✅ (.mjs or "type":"module") ✅ (primary) ✅ (primary)
npm packages ✅ (Deno 2.x)
node_modules ✅ (Deno 2.x)
package.json ✅ (Deno 2.x)
URL imports ✅ (original style)
JSR (new registry) Via npm shim ✅ (primary)
Workspace support ✅ (npm workspaces)

Deno 1.x used URL imports like import { serve } from "https://deno.land/std/http/server.ts". Deno 2.x kept this but added full npm compatibility — you can now import express from 'npm:express' or use a package.json normally.


Built-in APIs comparison

API Node.js Deno Bun
fetch ✅ (v18+)
WebSocket client ✅ (ws package)
WebSocket server ✅ (ws package) ✅ built-in
File system fs/promises Deno.readFile / fs Bun.file() / fs
Test runner node:test deno test bun test (Jest-compat)
Bundler ❌ (use webpack/esbuild) ✅ (deno bundle) ✅ (bun build)
SQLite ❌ (need better-sqlite3) ✅ (@db/sqlite) ✅ (bun:sqlite)
Password hashing ❌ (need bcrypt) ✅ (Bun.password)
S3 / object store ✅ (Bun.S3Client)
Watch mode --watch (v18+) --watch --hot (HMR)
Environment vars process.env Deno.env process.env / Bun.env

Security model

Aspect Node.js Deno Bun
Default access Full OS access Sandboxed Full OS access
Network restriction --allow-net=api.example.com
File system restriction --allow-read=/tmp
Environment restriction --allow-env=API_KEY
Audit tooling npm audit Built-in bun audit
Supply chain attack surface High (deep node_modules) Lower (explicit permissions) Medium
Permissions file deno.json "permissions"

Deno's permission model is ideal for running untrusted scripts, CI automation, or serverless functions where you want to enforce least-privilege at the runtime level. Node and Bun give code full system access by default — safety must be enforced at the OS/container level instead.


Ecosystem and tooling

Category Node.js Deno Bun
Package registry npm (2M+ packages) npm + JSR npm + JSR
Package manager speed npm (~30s) / pnpm (~8s) deno (~15s) bun (~3s)
Web framework Express, Fastify, Hono Hono, Oak, Fresh Hono, ElysiaJS, Elysia
Test runner Jest, Vitest deno test bun test (Jest API)
Linter/Formatter ESLint + Prettier Built-in deno lint / deno fmt ❌ (use ESLint/Prettier)
Bundler esbuild, Rollup, Webpack deno bundle / Vite bun build
ORM Prisma, Drizzle, TypeORM Prisma, Drizzle Prisma, Drizzle
Docker image size node:22-alpine (~60 MB) denoland/deno (~65 MB) oven/bun (~95 MB)
Hosting support Universal Deno Deploy, Cloudflare Vercel, Railway, Render
Cloud functions AWS Lambda, GCF, Azure Deno Deploy (edge) Cloudflare Workers (via adapter)

Where Node.js wins

Scenario Why Node.js
Legacy codebases Billions of existing Node projects, no migration risk
Maximum npm ecosystem Some packages with native bindings only work on Node
Enterprise / job market 99% of job listings and enterprises use Node
Hosting anywhere Every cloud, VPS, and PaaS supports Node
Large teams Deepest documentation, tutorials, StackOverflow answers
Mature framework ecosystem Express, NestJS, Fastify — battle-tested for years
Native modules (N-API) C++ addons like bcrypt, sharp work natively
Risk aversion Oldest, most predictable behavior

Where Deno wins

Scenario Why Deno
Security-critical scripts Fine-grained permissions prevent supply chain damage
TypeScript-first projects Zero-config TS, no build step for small scripts
CLI tools deno compile makes a single self-contained binary
Edge computing Deno Deploy is the most mature edge runtime
URL-based dependencies Import directly from CDN for quick scripts
Linting/formatting built-in deno lint and deno fmt eliminate config overhead
Web-standard APIs Closest to browser APIs — code ports between environments
Running untrusted code Sandbox model protects the host system

Where Bun wins

Scenario Why Bun
Raw HTTP throughput 2–3× faster than Node for high-concurrency APIs
Package install speed 10–25× faster than npm, critical for CI/CD
TypeScript scripts Sub-50ms startup makes it ideal for CLI tools
Monorepo local dev Faster installs and test runner speed up inner loop
Test runner replacement Jest-compatible API with faster execution
SQLite-heavy workloads bun:sqlite outperforms better-sqlite3
Minimal API servers ElysiaJS + Bun delivers top-tier req/s numbers
Rapid prototyping One binary, zero config, immediate TypeScript

Full comparison mega-table

Feature Node.js 22 Deno 2.x Bun 1.x
Engine V8 V8 JavaScriptCore
Written in C++ Rust Zig
Version (2025) 22.x LTS 2.x 1.x
TypeScript Manual Built-in Built-in
ESM support ✅ (awkward CJS/ESM mix) ✅ (primary) ✅ (primary)
npm packages
Security sandbox
Startup time ~60 ms ~40 ms ~8 ms
HTTP perf ~65k req/s ~110k req/s ~180k req/s
Package install ~30 s ~15 s ~3 s
Built-in test runner ✅ (node:test) ✅ (deno test) ✅ (bun test)
Built-in bundler
Built-in linter
Built-in formatter
Built-in SQLite
Single binary compile ❌ (use pkg/nexe)
Watch mode ✅ (with HMR)
Web APIs (fetch, etc.) ✅ (v18+)
Stability ✅✅✅ ✅✅
Ecosystem size ✅✅✅ ✅✅ ✅✅

Migration paths

Node.js → Bun

Most Node.js code runs on Bun without changes. Start with:

# Replace node with bun
bun run server.js   # instead of: node server.js
bun run index.ts    # TypeScript works natively

# Replace npm
bun install         # instead of: npm install
bun add express     # instead of: npm install express
bun run build       # same script names from package.json

Common gotchas:

  • Some native addons (N-API modules) may not work yet
  • __dirname / __filename work but use import.meta.dir idiomatically
  • Some Node.js built-in behaviors differ slightly (check Bun compatibility page)

Node.js → Deno 2.x

# Install Deno
curl -fsSL https://deno.land/install.sh | sh

# Run existing Node project (Deno 2 reads package.json)
deno run --allow-all server.ts

# Or install deps and run
deno install        # reads package.json, installs to node_modules
deno run server.ts  # then run with appropriate --allow-* flags

Common gotchas:

  • Need to add --allow-* flags (or --allow-all temporarily)
  • Some npm packages with native bindings may not work
  • Prefer deno.json over package.json for new Deno projects

Decision guide

Do you need maximum ecosystem compatibility?
  → Node.js

Do you run scripts that might include untrusted code?
  → Deno (permission model protects you)

Is install speed or test speed a bottleneck in CI?
  → Bun

Are you building a brand-new TypeScript API from scratch?
  → Bun (speed) or Deno (security + built-in tools)

Are you maintaining an existing Node.js app?
  → Stay on Node, or add Bun as a drop-in runner (low risk)

Do you need to deploy to edge computing?
  → Deno Deploy (most mature) or Cloudflare Workers

Common mistakes

Mistake Why it's a problem Fix
Assuming Bun = 100% Node.js compatible Some native N-API modules break Test before switching; check bun.sh/compatibility
Using --allow-all in Deno everywhere Defeats the security model Use fine-grained flags like --allow-net=api.example.com
Benchmarking hello-world and extrapolating Micro-benchmark ≠ real-world Benchmark your actual database + I/O stack
Using Bun in production before testing stability Bun 1.x is maturing; edge cases exist Pin Bun version; run full test suite before deploying
Migrating large teams to Deno for speed Deno ecosystem is smaller than Node's Measure real bottlenecks first; Node is plenty fast for most APIs
Ignoring Deno for CLI tools Deno compile makes distributable binaries Deno is excellent for TypeScript CLI tools
Installing Node.js toolchain alongside Bun Defeats the purpose Use bun as a full Node replacement for new projects
Not using bun test when already on Bun Missing 2–5× faster test runs vs Jest Replace Jest with bun test — API is compatible

Node.js vs Deno vs Bun vs other runtimes

Runtime Engine Best for Maturity npm compat
Node.js V8 Production APIs, enterprise ✅✅✅ ✅ (native)
Deno V8 Scripts, edge, security ✅✅ ✅ (v2+)
Bun JSC Speed-critical, developer tooling
Cloudflare Workers V8 (isolates) Edge CDN functions ✅✅ Partial
WinterJS SpiderMonkey WASI/embedded Beta Partial
LLRT (AWS) QuickJS Lambda cold starts Experimental Partial

Frequently asked questions

Should I switch my production Node.js app to Bun? Not without testing. Bun is compatible with most Node.js code, but subtle differences in built-in module behavior and unimplemented N-API modules can cause silent bugs. Run your full test suite on Bun first. For new projects, Bun is a safe bet. For legacy production apps, stick with Node unless you have a specific performance problem.

Is Deno 2.x finally compatible with npm packages? Yes — Deno 2.x added full npm compatibility including package.json, node_modules, and npm: specifiers. You can run most Node.js applications on Deno 2.x with minimal changes. The main remaining gaps are packages with native bindings.

Why does Bun use JavaScriptCore instead of V8? JSC (WebKit's engine, used in Safari) has faster startup times and often better single-threaded performance for certain workloads. V8 (Chrome/Node/Deno) has more aggressive JIT optimizations and a larger optimization community. For Bun's use case (fast scripts, tooling), JSC's startup advantage matters more.

Can I use Deno for serious production backends? Absolutely. Deno Deploy (edge), Deno's HTTP server, and its npm compatibility make it production-capable. Companies like Netlify use Deno internally. The ecosystem is smaller than Node's but growing, and Deno 2.x closed most of the compatibility gaps.

Which has the best TypeScript developer experience? Deno and Bun both run TypeScript natively without configuration. Deno additionally ships a built-in linter (deno lint) and formatter (deno fmt), making it the most zero-config TypeScript environment. Bun is faster but requires external linting/formatting tools. Node requires the most setup but has the richest IDE tooling ecosystem.

Will Bun or Deno replace Node.js? Unlikely in the near term. Node.js has 15 years of ecosystem momentum, universal hosting support, and massive adoption. What's more likely: Bun becomes the default package manager and test runner for many Node.js projects (already happening), and Deno captures the edge computing and scripting niches. Node's core will continue improving — Node 22 added native TypeScript stripping, narrowing the gap.

Related tools

Keep reading

All Toolmingotools are free & run in your browser

No sign-up, no upload, no watermark. Your files never leave your device.

Browse all tools