Bun Node.js Compatibility

One of Bun's design goals is compatibility with the Node.js ecosystem. This chapter introduces Bun's Node.js compatibility and migration guide.

Compatibility Overview

Supported Node.js APIs

Bun supports most Node.js core modules:

ModuleCompatibilityDescription
fs✅ CompleteFile system
path✅ CompletePath operations
http✅ MostHTTP server and client
https✅ MostHTTPS support
crypto✅ MostCryptographic functions
buffer✅ CompleteBuffer operations
stream✅ MostStream processing
events✅ CompleteEvent emitter
util✅ MostUtility functions
os✅ CompleteOperating system info
child_process✅ MostChild processes
worker_threads✅ MostWorker threads
net✅ MostNetwork sockets
dns✅ MostDNS resolution
url✅ CompleteURL processing
querystring✅ CompleteQuery string
zlib✅ MostCompression
assert✅ CompleteAssertions

Partially Supported APIs

ModuleStatusDescription
cluster⚠️ PartialLimited cluster mode support
vm⚠️ PartialLimited VM module support
inspector⚠️ PartialDebugger support
trace_events❌ UnsupportedTrace events
v8❌ UnsupportedV8 specific APIs

Using Node.js APIs

Import Methods

// Recommended: use node: prefix
import fs from "node:fs";
import path from "node:path";
import { EventEmitter } from "node:events";

// Also supported: without prefix
import crypto from "crypto";
import http from "http";

File System

import fs from "node:fs";
import { readFile, writeFile } from "node:fs/promises";

// Synchronous read
const content = fs.readFileSync("./file.txt", "utf-8");

// Async read
const asyncContent = await readFile("./file.txt", "utf-8");

// Write file
await writeFile("./output.txt", "Hello, Bun!");

// Stream read
const stream = fs.createReadStream("./large-file.txt");
for await (const chunk of stream) {
  console.log(chunk);
}

HTTP Server

import http from "node:http";

// Node.js style HTTP server
const server = http.createServer((req, res) => {
  res.writeHead(200, { "Content-Type": "text/plain" });
  res.end("Hello from Node.js API!");
});

server.listen(3000, () => {
  console.log("Server running at http://localhost:3000");
});

Child Processes

import { spawn, exec } from "node:child_process";

// Use spawn
const child = spawn("ls", ["-la"]);

child.stdout.on("data", (data) => {
  console.log(`stdout: ${data}`);
});

// Use exec
exec("echo 'Hello'", (error, stdout, stderr) => {
  if (error) {
    console.error(`Error: ${error}`);
    return;
  }
  console.log(`Output: ${stdout}`);
});

// Use Promise (recommended)
import { execSync } from "node:child_process";
const result = execSync("echo 'Hello'").toString();

Cryptography

import crypto from "node:crypto";

// Hash
const hash = crypto.createHash("sha256")
  .update("Hello, World!")
  .digest("hex");
console.log("SHA256:", hash);

// HMAC
const hmac = crypto.createHmac("sha256", "secret")
  .update("message")
  .digest("hex");

// Random bytes
const randomBytes = crypto.randomBytes(16);
console.log("Random bytes:", randomBytes.toString("hex"));

// UUID
const uuid = crypto.randomUUID();
console.log("UUID:", uuid);

Migrating from Node.js

Step 1: Install Bun

# Install Bun
curl -fsSL https://bun.sh/install | bash

# Verify installation
bun --version

Step 2: Switch Package Manager

# Remove old dependencies
rm -rf node_modules package-lock.json yarn.lock pnpm-lock.yaml

# Install with Bun
bun install

Step 3: Update package.json

{
  "scripts": {
    "start": "bun src/index.ts",
    "dev": "bun --watch src/index.ts",
    "build": "bun build src/index.ts --outdir dist",
    "test": "bun test"
  }
}

Step 4: Check Compatibility

# Try running
bun src/index.ts

# Check for errors

Common Migration Issues

Issue 1: Module Not Found

// ❌ May throw error
import { something } from "obscure-package";

// ✅ Check package compatibility
bun pm ls obscure-package

Issue 2: Native Modules

// Some native Node.js modules may not be compatible
// Need to find alternatives or wait for Bun support

// For example: sharp image processing
// Check Bun support: https://bun.sh/docs/ecosystem

Issue 3: Node.js Specific APIs

// ❌ Some Node.js specific APIs may not exist
process.binding("buffer");

// ✅ Use standard API alternatives
import { Buffer } from "node:buffer";

Issue 4: __dirname and __filename

// In ESM, __dirname and __filename are not available

// ✅ Bun supports these
console.log(__dirname);  // Available
console.log(__filename); // Available

// Or use import.meta
console.log(import.meta.dir);  // Directory
console.log(import.meta.path); // File path

Bun vs Node.js API Comparison

File Operations

// Node.js
import { readFile } from "fs/promises";
const content = await readFile("./file.txt", "utf-8");

// Bun (more concise)
const content = await Bun.file("./file.txt").text();

HTTP Server

// Node.js
import http from "http";
const server = http.createServer((req, res) => {
  res.writeHead(200);
  res.end("Hello");
});
server.listen(3000);

// Bun (more concise)
Bun.serve({
  port: 3000,
  fetch: () => new Response("Hello"),
});

Environment Variables

// Node.js
require("dotenv").config();
const value = process.env.MY_VAR;

// Bun (auto loads .env)
const value = Bun.env.MY_VAR;
// or
const value = process.env.MY_VAR;

Running TypeScript

# Node.js
npx ts-node app.ts
# or
npx tsx app.ts

# Bun (direct run)
bun app.ts

Using Bun Alternatives

Replacing Express

// Use Elysia (Bun-specific framework)
import { Elysia } from "elysia";

const app = new Elysia()
  .get("/", () => "Hello Elysia")
  .get("/user/:id", ({ params: { id } }) => `User ${id}`)
  .post("/user", ({ body }) => body)
  .listen(3000);

console.log(`Running at http://localhost:${app.server?.port}`);

Replacing Jest

// Bun built-in testing
import { test, expect } from "bun:test";

test("addition", () => {
  expect(1 + 1).toBe(2);
});

Replacing webpack/esbuild

// Bun built-in bundling
await Bun.build({
  entrypoints: ["./src/index.ts"],
  outdir: "./dist",
  minify: true,
});

Progressive Migration

Strategy 1: Partial Bun Usage

{
  "scripts": {
    "start": "node dist/index.js",
    "dev": "bun --watch src/index.ts",
    "test": "bun test",
    "build": "bun build src/index.ts --outdir dist"
  }
}

Strategy 2: Conditional Usage

// Detect runtime
const isBun = typeof Bun !== "undefined";

if (isBun) {
  // Use Bun API
  const content = await Bun.file("./data.txt").text();
} else {
  // Use Node.js API
  const { readFile } = await import("fs/promises");
  const content = await readFile("./data.txt", "utf-8");
}

Strategy 3: Abstraction Layer

// file-utils.ts
export async function readTextFile(path: string): Promise<string> {
  if (typeof Bun !== "undefined") {
    return Bun.file(path).text();
  }
  const { readFile } = await import("fs/promises");
  return readFile(path, "utf-8");
}

Compatibility Check Tools

Check Package Compatibility

# View package compatibility status
# Visit https://bun.sh/ecosystem

Test Run

# Run project and observe errors
bun run start 2>&1 | head -50

# Run tests
bun test

Known Limitations

Incompletely Supported Features

  1. cluster module: Limited support
  2. vm module: Partial functionality
  3. Some Node.js internal APIs
  4. Specific native modules

Solutions

// If you encounter unsupported features
// 1. Check Bun docs for alternatives
// 2. Use polyfills
// 3. Conditional loading
// 4. Wait for Bun updates

// Example: conditional cluster usage
if (typeof Bun !== "undefined") {
  // Use Bun's multi-process solution
} else {
  // Use Node.js cluster
  const cluster = require("cluster");
  // ...
}

Summary

This chapter covered:

  • ✅ Bun's compatibility with Node.js APIs
  • ✅ How to use Node.js modules
  • ✅ Steps to migrate from Node.js
  • ✅ Common migration issues and solutions
  • ✅ Bun alternatives
  • ✅ Progressive migration strategies

Next Steps

Continue reading Learning Resources for more learning materials.