Tools
Tools: Bun vs Deno vs Node.js in 2026: Benchmarks, Code, and Real Numbers
2026-01-21
0 views
admin
The Engine Difference ## HTTP Throughput (Express, same code) ## Package Installation ## TypeScript Execution ## Cold Start (AWS Lambda) ## Memory Usage ## Security Model ## Node.js API Compatibility ## Built-in Tools ## When to Use What ## Migration Path Three runtimes. Three engines. One question: which one should you actually use? I ran benchmarks, tested compatibility, and migrated real projects. Here's what I found. V8 optimizes for long-running processes. JavaScriptCore optimizes for fast startup. This single difference explains most benchmark results. Using native APIs instead of Express: Bun native: 68,000 req/sec. The gap widens. Real monorepo, 1,847 dependencies: Bun uses binary lockfile (bun.lockb) and global cache. No node_modules bloat on repeated installs. Node.js type stripping doesn't transpile. No enums, no decorators, no namespace. For production Node.js, you still need tsc or esbuild. 35% faster cold starts = 35% lower Lambda bills on compute-heavy workloads. Bun 1.3 reduced memory 10-30% in Next.js and Elysia apps. Real numbers from production: Deno sandboxes everything. Compromised npm package can't exfiltrate data without --allow-net. Node.js and Bun trust all code completely. Bun 1.2+ implements most Node.js APIs: Edge cases break: native addons, obscure Node internals, some streams behavior. Deno 2 added npm: specifiers: Works, but CommonJS packages sometimes need adjustments. Bun test runner is 20x faster than Jest. Same syntax: Node.js: Legacy codebases, maximum npm compatibility, enterprise LTS requirements Deno: Security-critical apps, greenfield TypeScript projects, edge deployment via Deno Deploy Bun: Performance-critical APIs, fast local development, serverless where cold start matters Lowest risk approach: Most Express/Fastify apps run on Bun without code changes. Bun is fastest. Deno is most secure. Node.js is most compatible. All three are production-ready in 2026. Pick based on your constraints, not Twitter hype. Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to ? It will become hidden in your post, but will still be visible via the comment's permalink. as well , this person and/or CODE_BLOCK: Node.js → V8 (Chrome) → C++ + libuv Deno → V8 (Chrome) → Rust Bun → JavaScriptCore (Safari) → Zig + io_uring CODE_BLOCK: Node.js → V8 (Chrome) → C++ + libuv Deno → V8 (Chrome) → Rust Bun → JavaScriptCore (Safari) → Zig + io_uring CODE_BLOCK: Node.js → V8 (Chrome) → C++ + libuv Deno → V8 (Chrome) → Rust Bun → JavaScriptCore (Safari) → Zig + io_uring CODE_BLOCK: Bun: 52,000 req/sec Deno: 29,000 req/sec Node.js: 14,000 req/sec CODE_BLOCK: Bun: 52,000 req/sec Deno: 29,000 req/sec Node.js: 14,000 req/sec CODE_BLOCK: Bun: 52,000 req/sec Deno: 29,000 req/sec Node.js: 14,000 req/sec COMMAND_BLOCK: // Bun Bun.serve({ port: 3000, fetch: () => new Response("Hello") }); // Deno Deno.serve({ port: 3000 }, () => new Response("Hello")); // Node.js require('http').createServer((req, res) => res.end("Hello") ).listen(3000); COMMAND_BLOCK: // Bun Bun.serve({ port: 3000, fetch: () => new Response("Hello") }); // Deno Deno.serve({ port: 3000 }, () => new Response("Hello")); // Node.js require('http').createServer((req, res) => res.end("Hello") ).listen(3000); COMMAND_BLOCK: // Bun Bun.serve({ port: 3000, fetch: () => new Response("Hello") }); // Deno Deno.serve({ port: 3000 }, () => new Response("Hello")); // Node.js require('http').createServer((req, res) => res.end("Hello") ).listen(3000); COMMAND_BLOCK: npm install: 28 minutes pnpm install: 4 minutes bun install: 47 seconds COMMAND_BLOCK: npm install: 28 minutes pnpm install: 4 minutes bun install: 47 seconds COMMAND_BLOCK: npm install: 28 minutes pnpm install: 4 minutes bun install: 47 seconds COMMAND_BLOCK: # Bun - just works bun index.ts # Deno - just works deno run index.ts # Node.js - needs flag (experimental, strips types only) node --experimental-strip-types index.ts COMMAND_BLOCK: # Bun - just works bun index.ts # Deno - just works deno run index.ts # Node.js - needs flag (experimental, strips types only) node --experimental-strip-types index.ts COMMAND_BLOCK: # Bun - just works bun index.ts # Deno - just works deno run index.ts # Node.js - needs flag (experimental, strips types only) node --experimental-strip-types index.ts CODE_BLOCK: Node.js: 245ms average Bun: 156ms average CODE_BLOCK: Node.js: 245ms average Bun: 156ms average CODE_BLOCK: Node.js: 245ms average Bun: 156ms average CODE_BLOCK: Next.js on Node.js: 512MB baseline Next.js on Bun: 380MB baseline CODE_BLOCK: Next.js on Node.js: 512MB baseline Next.js on Bun: 380MB baseline CODE_BLOCK: Next.js on Node.js: 512MB baseline Next.js on Bun: 380MB baseline CODE_BLOCK: // Deno - explicit permissions required deno run --allow-net --allow-read server.ts // Bun/Node.js - full system access by default bun server.ts node server.ts CODE_BLOCK: // Deno - explicit permissions required deno run --allow-net --allow-read server.ts // Bun/Node.js - full system access by default bun server.ts node server.ts CODE_BLOCK: // Deno - explicit permissions required deno run --allow-net --allow-read server.ts // Bun/Node.js - full system access by default bun server.ts node server.ts CODE_BLOCK: // Works in Bun import fs from 'fs'; import path from 'path'; import { Buffer } from 'buffer'; import express from 'express'; CODE_BLOCK: // Works in Bun import fs from 'fs'; import path from 'path'; import { Buffer } from 'buffer'; import express from 'express'; CODE_BLOCK: // Works in Bun import fs from 'fs'; import path from 'path'; import { Buffer } from 'buffer'; import express from 'express'; CODE_BLOCK: // Deno import express from "npm:express"; CODE_BLOCK: // Deno import express from "npm:express"; CODE_BLOCK: // Deno import express from "npm:express"; CODE_BLOCK: | Node.js | Deno | Bun --------------|---------|------|----- Package mgr | ✗ | ✓ | ✓ Bundler | ✗ | ✓ | ✓ Test runner | ✓ | ✓ | ✓ TypeScript | ~ | ✓ | ✓ Formatter | ✗ | ✓ | ✗ Linter | ✗ | ✓ | ✗ CODE_BLOCK: | Node.js | Deno | Bun --------------|---------|------|----- Package mgr | ✗ | ✓ | ✓ Bundler | ✗ | ✓ | ✓ Test runner | ✓ | ✓ | ✓ TypeScript | ~ | ✓ | ✓ Formatter | ✗ | ✓ | ✗ Linter | ✗ | ✓ | ✗ CODE_BLOCK: | Node.js | Deno | Bun --------------|---------|------|----- Package mgr | ✗ | ✓ | ✓ Bundler | ✗ | ✓ | ✓ Test runner | ✓ | ✓ | ✓ TypeScript | ~ | ✓ | ✓ Formatter | ✗ | ✓ | ✗ Linter | ✗ | ✓ | ✗ COMMAND_BLOCK: import { test, expect } from "bun:test"; test("math works", () => { expect(2 + 2).toBe(4); }); COMMAND_BLOCK: import { test, expect } from "bun:test"; test("math works", () => { expect(2 + 2).toBe(4); }); COMMAND_BLOCK: import { test, expect } from "bun:test"; test("math works", () => { expect(2 + 2).toBe(4); }); COMMAND_BLOCK: # Step 1: Use Bun for dev, deploy to Node.js bun install # instead of npm install bun test # instead of jest bun run dev # same scripts work # Step 2: Test production compatibility bun build ./src/index.ts --outdir ./dist # Step 3: Switch runtime when confident COMMAND_BLOCK: # Step 1: Use Bun for dev, deploy to Node.js bun install # instead of npm install bun test # instead of jest bun run dev # same scripts work # Step 2: Test production compatibility bun build ./src/index.ts --outdir ./dist # Step 3: Switch runtime when confident COMMAND_BLOCK: # Step 1: Use Bun for dev, deploy to Node.js bun install # instead of npm install bun test # instead of jest bun run dev # same scripts work # Step 2: Test production compatibility bun build ./src/index.ts --outdir ./dist # Step 3: Switch runtime when confident COMMAND_BLOCK: # Try it yourself curl -fsSL https://bun.sh/install | bash bun --version COMMAND_BLOCK: # Try it yourself curl -fsSL https://bun.sh/install | bash bun --version COMMAND_BLOCK: # Try it yourself curl -fsSL https://bun.sh/install | bash bun --version
toolsutilitiessecurity toolsbenchmarksnumbersenginedifferencethroughput