Bun 1.2 Released - Full Picture of Node.js Compatibility and Performance Optimization

2025.12.02

Bun is an ultra-fast JavaScript runtime and package manager written in Zig. In Bun 1.2, Node.js compatibility has significantly improved, accelerating enterprise adoption.

Major New Features in Bun 1.2

Overview

flowchart TB
    subgraph Bun12["Bun 1.2"]
        subgraph Compat["Node.js Compatibility"]
            C1["node:cluster Full Support"]
            C2["node:dgram (UDP) Support"]
            C3["node:v8 Partial Support"]
            C4["90%+ npm packages work"]
        end

        subgraph Features["New Features"]
            F1["Built-in S3 Client"]
            F2["Bun.color() Color Processing API"]
            F3["Improved Watch Mode"]
            F4["HTML/CSS Bundler"]
        end

        subgraph Perf["Performance"]
            P1["HTTP Server: 30% faster"]
            P2["Memory Usage: 20% reduction"]
            P3["Package Install: 2x faster"]
        end
    end

Node.js Compatibility Improvements

node:cluster Support

// cluster-server.ts - Multi-process server
import cluster from 'node:cluster';
import { cpus } from 'node:os';
import { serve } from 'bun';

const numCPUs = cpus().length;

if (cluster.isPrimary) {
  console.log(`Primary ${process.pid} is running`);
  console.log(`Forking ${numCPUs} workers...`);

  // Fork workers for each CPU core
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} died`);
    // Auto-restart
    cluster.fork();
  });

  // Receive messages from workers
  cluster.on('message', (worker, message) => {
    console.log(`Message from worker ${worker.process.pid}:`, message);
  });
} else {
  // Worker process
  const server = serve({
    port: 3000,
    fetch(req) {
      return new Response(`Hello from worker ${process.pid}`);
    },
  });

  console.log(`Worker ${process.pid} started on port ${server.port}`);

  // Send message to primary
  process.send?.({ status: 'ready', pid: process.pid });
}

node:dgram (UDP) Support

// udp-server.ts
import dgram from 'node:dgram';

const server = dgram.createSocket('udp4');

server.on('error', (err) => {
  console.error(`Server error:\n${err.stack}`);
  server.close();
});

server.on('message', (msg, rinfo) => {
  console.log(`Server got: ${msg} from ${rinfo.address}:${rinfo.port}`);

  // Send response
  const response = Buffer.from(`Received: ${msg}`);
  server.send(response, rinfo.port, rinfo.address);
});

server.on('listening', () => {
  const address = server.address();
  console.log(`UDP server listening on ${address.address}:${address.port}`);
});

server.bind(41234);

// udp-client.ts
const client = dgram.createSocket('udp4');
const message = Buffer.from('Hello UDP Server');

client.send(message, 41234, 'localhost', (err) => {
  if (err) {
    console.error('Send error:', err);
    client.close();
    return;
  }
  console.log('Message sent');
});

client.on('message', (msg) => {
  console.log(`Received: ${msg}`);
  client.close();
});

Built-in S3 Client

Simplified S3 Operations

// Bun.s3 - Native S3 client

// Automatically get credentials from environment variables
// AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION

// File upload
const file = Bun.file('./large-file.zip');
await Bun.s3.write('my-bucket/uploads/large-file.zip', file);

// Streaming upload
const response = await fetch('https://example.com/large-video.mp4');
await Bun.s3.write('my-bucket/videos/video.mp4', response);

// Write text data
await Bun.s3.write(
  'my-bucket/data/config.json',
  JSON.stringify({ setting: 'value' }),
  { contentType: 'application/json' }
);

// Read file
const data = await Bun.s3.file('my-bucket/data/config.json').text();
const config = JSON.parse(data);

// Read as ArrayBuffer
const buffer = await Bun.s3.file('my-bucket/images/photo.jpg').arrayBuffer();

// Streaming read
const stream = Bun.s3.file('my-bucket/large-data.csv').stream();

// Get file info
const s3File = Bun.s3.file('my-bucket/document.pdf');
console.log({
  exists: await s3File.exists(),
  size: await s3File.size,
  lastModified: await s3File.lastModified,
});

// Delete file
await Bun.s3.delete('my-bucket/temp/old-file.txt');

// Generate signed URL
const signedUrl = await Bun.s3.presign('my-bucket/private/file.pdf', {
  expiresIn: 3600, // 1 hour
  method: 'GET',
});

// List objects
for await (const object of Bun.s3.list('my-bucket/uploads/')) {
  console.log(object.key, object.size, object.lastModified);
}

S3 File Server

// s3-file-server.ts
import { serve } from 'bun';

serve({
  port: 3000,

  async fetch(req) {
    const url = new URL(req.url);
    const path = url.pathname.slice(1); // Remove leading /

    if (req.method === 'GET') {
      // Stream file directly from S3
      const s3File = Bun.s3.file(`my-bucket/${path}`);

      if (!(await s3File.exists())) {
        return new Response('Not Found', { status: 404 });
      }

      return new Response(s3File.stream(), {
        headers: {
          'Content-Type': s3File.type || 'application/octet-stream',
          'Content-Length': String(await s3File.size),
        },
      });
    }

    if (req.method === 'PUT') {
      // Stream request body to S3
      await Bun.s3.write(`my-bucket/${path}`, req);
      return new Response('Uploaded', { status: 201 });
    }

    if (req.method === 'DELETE') {
      await Bun.s3.delete(`my-bucket/${path}`);
      return new Response('Deleted', { status: 200 });
    }

    return new Response('Method Not Allowed', { status: 405 });
  },
});

Bun.color() API

New Color Processing API

// Bun.color() - High-speed color processing

// Parse CSS color string
const red = Bun.color('red');
console.log(red); // { r: 255, g: 0, b: 0, a: 1 }

// Convert from HEX
const hex = Bun.color('#3498db');
console.log(hex); // { r: 52, g: 152, b: 219, a: 1 }

// Convert from RGB
const rgb = Bun.color('rgb(100, 150, 200)');
console.log(rgb); // { r: 100, g: 150, b: 200, a: 1 }

// Convert from HSL
const hsl = Bun.color('hsl(210, 60%, 50%)');
console.log(hsl); // { r: ..., g: ..., b: ..., a: 1 }

// Output as array
const [r, g, b, a] = Bun.color('#ff6b6b', 'array');
console.log(r, g, b, a); // 255, 107, 107, 1

// Convert to HEX string
const hexString = Bun.color({ r: 255, g: 100, b: 50, a: 1 }, 'hex');
console.log(hexString); // '#ff6432'

// Convert to CSS format
const cssRgb = Bun.color({ r: 255, g: 100, b: 50, a: 0.5 }, 'css');
console.log(cssRgb); // 'rgba(255, 100, 50, 0.5)'

// Color manipulation
function lighten(color: string, amount: number): string {
  const { r, g, b, a } = Bun.color(color)!;
  return Bun.color({
    r: Math.min(255, r + amount),
    g: Math.min(255, g + amount),
    b: Math.min(255, b + amount),
    a,
  }, 'hex');
}

function darken(color: string, amount: number): string {
  const { r, g, b, a } = Bun.color(color)!;
  return Bun.color({
    r: Math.max(0, r - amount),
    g: Math.max(0, g - amount),
    b: Math.max(0, b - amount),
    a,
  }, 'hex');
}

console.log(lighten('#3498db', 30)); // Lighter
console.log(darken('#3498db', 30));  // Darker

Improved Watch Mode

Enhanced File Watching

// bun --watch for dev server

// package.json
{
  "scripts": {
    "dev": "bun --watch src/index.ts",
    "dev:hot": "bun --hot src/server.ts"
  }
}

// --watch: Process restart
// --hot: Hot reload (preserves state)
// hot-reload-server.ts
import { serve } from 'bun';

// Global state (preserved during hot reload)
declare global {
  var requestCount: number;
}
globalThis.requestCount ??= 0;

serve({
  port: 3000,

  fetch(req) {
    globalThis.requestCount++;

    return new Response(`
      <html>
        <body>
          <h1>Hot Reload Demo</h1>
          <p>Request count: ${globalThis.requestCount}</p>
          <p>Last updated: ${new Date().toISOString()}</p>
        </body>
      </html>
    `, {
      headers: { 'Content-Type': 'text/html' },
    });
  },
});

console.log('Server started on port 3000');

Programmatic File Watching

// Bun.watch() - File watching API
const watcher = Bun.watch({
  paths: ['./src'],
  recursive: true,
  filter: (path) => path.endsWith('.ts') || path.endsWith('.tsx'),
});

for await (const event of watcher) {
  console.log(`${event.type}: ${event.path}`);

  if (event.type === 'change' || event.type === 'create') {
    // Handle file change
    await runTests(event.path);
  }

  if (event.type === 'delete') {
    // Handle file deletion
    console.log(`File deleted: ${event.path}`);
  }
}

async function runTests(changedFile: string) {
  const testFile = changedFile.replace('.ts', '.test.ts');
  if (await Bun.file(testFile).exists()) {
    const proc = Bun.spawn(['bun', 'test', testFile]);
    await proc.exited;
  }
}

HTML Bundler

Simplified Frontend Builds

// bun build --html

// Input: index.html
// <!DOCTYPE html>
// <html>
// <head>
//   <link rel="stylesheet" href="./styles.css">
// </head>
// <body>
//   <div id="app"></div>
//   <script type="module" src="./app.tsx"></script>
// </body>
// </html>

// Build command
// bun build ./index.html --outdir=./dist

// Result:
// - Auto-detect CSS/JS in HTML
// - Auto-compile TypeScript/TSX
// - Bundle and optimize CSS
// - Resolve dependencies
// Programmatic HTML build
const result = await Bun.build({
  entrypoints: ['./src/index.html'],
  outdir: './dist',
  minify: true,
  sourcemap: 'external',
  splitting: true,
  target: 'browser',
  define: {
    'process.env.NODE_ENV': '"production"',
  },
  loader: {
    '.png': 'file',
    '.svg': 'file',
    '.woff2': 'file',
  },
});

if (!result.success) {
  console.error('Build failed:');
  for (const log of result.logs) {
    console.error(log);
  }
  process.exit(1);
}

console.log('Build outputs:');
for (const output of result.outputs) {
  console.log(`  ${output.path} (${output.size} bytes)`);
}

Performance Improvements

Benchmark Comparison

HTTP Server Performance (req/sec):

RuntimeHello WorldJSON APIFile Serving
Bun 1.2250,000180,000150,000
Bun 1.1190,000140,000120,000
Node.js 2075,00055,00045,000
Deno 1.4095,00070,00060,000

Package Install Time (node_modules):

ToolCleanWith Cache
bun install2.1s0.3s
npm install15.2s8.5s
yarn install12.8s4.2s
pnpm install8.5s1.8s

Optimized API Usage Example

// High-speed JSON server
import { serve } from 'bun';

const users = new Map<string, User>();

serve({
  port: 3000,

  async fetch(req) {
    const url = new URL(req.url);

    // Bun.router() - High-speed routing (future feature)
    if (url.pathname.startsWith('/api/users')) {
      const userId = url.pathname.split('/')[3];

      if (req.method === 'GET') {
        if (userId) {
          const user = users.get(userId);
          if (!user) {
            return Response.json({ error: 'Not found' }, { status: 404 });
          }
          return Response.json(user);
        }
        return Response.json([...users.values()]);
      }

      if (req.method === 'POST') {
        const body = await req.json();
        const id = crypto.randomUUID();
        const user = { id, ...body, createdAt: new Date().toISOString() };
        users.set(id, user);
        return Response.json(user, { status: 201 });
      }
    }

    return new Response('Not Found', { status: 404 });
  },
});

Bun Native Features

Bun.sql (High-Speed SQLite Operations)

// Bun.sql - Built-in SQLite driver

import { Database } from 'bun:sqlite';

const db = new Database(':memory:');

// Create table
db.run(`
  CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    email TEXT UNIQUE NOT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
  )
`);

// Prepared statements
const insertUser = db.prepare(`
  INSERT INTO users (name, email) VALUES ($name, $email)
`);

const getUser = db.prepare(`
  SELECT * FROM users WHERE id = $id
`);

const getAllUsers = db.prepare(`
  SELECT * FROM users ORDER BY created_at DESC
`);

// Insert data
insertUser.run({ $name: 'Alice', $email: 'alice@example.com' });
insertUser.run({ $name: 'Bob', $email: 'bob@example.com' });

// Get data
const user = getUser.get({ $id: 1 });
console.log(user); // { id: 1, name: 'Alice', ... }

const users = getAllUsers.all();
console.log(users);

// Transaction
const insertMany = db.transaction((users: Array<{ name: string; email: string }>) => {
  for (const user of users) {
    insertUser.run({ $name: user.name, $email: user.email });
  }
});

insertMany([
  { name: 'Charlie', email: 'charlie@example.com' },
  { name: 'Diana', email: 'diana@example.com' },
]);

Bun.password (Password Hashing)

// Bun.password - Secure password hashing

// Hash password
const hash = await Bun.password.hash('my-secure-password', {
  algorithm: 'argon2id', // bcrypt, argon2id, argon2i, argon2d
  memoryCost: 65536,     // 64MB
  timeCost: 3,
});

console.log(hash);
// $argon2id$v=19$m=65536,t=3,p=1$...

// Verify password
const isValid = await Bun.password.verify(
  'my-secure-password',
  hash
);
console.log(isValid); // true

// Using bcrypt
const bcryptHash = await Bun.password.hash('password123', {
  algorithm: 'bcrypt',
  cost: 12,
});

const bcryptValid = await Bun.password.verify('password123', bcryptHash);

Bun.sleep and Timers

// Bun.sleep - High-precision sleep

// Millisecond specification
await Bun.sleep(1000); // Wait 1 second

// Named timer (cancellable)
const timer = Bun.sleep(5000);
setTimeout(() => timer.cancel(), 2000); // Cancel after 2 seconds

try {
  await timer;
} catch (e) {
  console.log('Timer was cancelled');
}

// sleepSync (synchronous version)
Bun.sleepSync(100); // Synchronous wait 100ms

Migration Guide

Migrating from Node.js

// package.json
{
  "scripts": {
    // Node.js
    "dev:node": "node --watch src/index.js",
    "build:node": "tsc && node dist/index.js",

    // Bun (direct TypeScript execution)
    "dev": "bun --watch src/index.ts",
    "build": "bun build src/index.ts --outdir=dist",
    "start": "bun dist/index.js"
  }
}
// Compatibility check
// bun pm untrusted - Check untrusted packages
// bun pm verify    - Verify package integrity

// Common compatibility issues and solutions:

// 1. __dirname / __filename
// Node.js: Available globally
// Bun: Use import.meta in ESM
const __dirname = import.meta.dir;
const __filename = import.meta.file;

// 2. require() in ESM
// Bun uses import.meta.require()
const pkg = import.meta.require('./package.json');

// 3. Native addons
// Some native modules are incompatible
// Alternative: Bun native API or WASM

Express to Elysia Migration Example

// Express (Node.js)
import express from 'express';
const app = express();

app.get('/users/:id', (req, res) => {
  const { id } = req.params;
  res.json({ id, name: 'User' });
});

app.listen(3000);

// Elysia (Bun-optimized framework)
import { Elysia } from 'elysia';

const app = new Elysia()
  .get('/users/:id', ({ params: { id } }) => ({
    id,
    name: 'User',
  }))
  .listen(3000);

console.log(`Server running at ${app.server?.hostname}:${app.server?.port}`);

Summary

Bun 1.2 has reached a practical level as a Node.js alternative.

Bun 1.2 Strengths

FeatureBenefit
Ultra-fast startupFaster development cycles
Direct TypeScript executionNo build step needed
All-in-oneRuntime + Bundler + PM
Node.js compatibleReuse existing code

Adoption Decision Criteria

  • New projects: Can be actively adopted
  • Existing Node.js projects: Migrate after compatibility testing
  • Enterprise: Gradual introduction recommended

Bun is becoming the new standard for JavaScript/TypeScript development.

← Back to list