Introduction: What is Bun and why is everyone talking about it?
Bun is a new JavaScript runtime that promises to be much faster than Node.js and Deno. Its developers claim that the installation of packages is 10-20 times faster, and the launch of applications is 3-4 times faster. Sounds tempting, doesn't it? But what happens when you decide to move a real project to Bun?
In this article, I will talk about the real problems that developers face when migrating, and how to solve them.
What does Bun promise?
Before we talk about the problems, let's figure out what Bun offers in general:
Speed: Written in Zig, uses JavaScriptCore instead of V8
Built-in tools: bundler, transpiler, package manager in one bottle
Compatibility: Support for most Node.js APIs is claimed
TypeScript out of the box: No need for a separate compiler
Web API: Support for modern browser APIs on the server
Sounds perfect. But practice shows otherwise.
Problem #1: Incomplete compatibility with Node.js
What is expected?
Bun developers promise 90%+ compatibility with the Node.js API. In theory, your code should just work.
Reality:
// This code works in Node.js
const fs = require('fs');
fs.watch('./files', { recursive: true }, (event, filename) => {
console.log(`${filename} changed`);
});In Bun, the recursive option for fs.watch() does not work on some operating systems. You will receive an error or silent failure.
Solution:
Check the Bun documentation for each API you use. It is often necessary to use alternative libraries:
// Alternative to Bun
import { watch } from 'chokidar';
watch('./files', {
ignoreInitial: true
}).on('all', (event, path) => {
console.log(`${path} changed`);
});Problem #2: npm packages with native modules.
What is expected?
Bun must support most npm packages, including those that use native modules.
Reality:
Many popular packages simply do not work:
bun install sharp # Popular library for working with imagesWhen trying to use:
import sharp from 'sharp';
const image = sharp('input.jpg');
// Error: Cannot find module "sharp"Problems with other packages:
bcrypt — native bindings are not supported
node-gyp dependencies — require a complete rebuild
sqlite3 — unstable operation
Solution:
Look for alternatives in pure JavaScript:
// Use bcryptjs instead of bcrypt
import bcrypt from 'bcryptjs';
const hash = await bcrypt.hash('password', 10);
// Instead of sharp, you can use Bun.file() + Canvas API
import { createCanvas, loadImage } from 'canvas';Problem #3: Differences in EventEmitter behavior
What is expected?
EventEmitter should work exactly the same as in Node.js.
Reality:
const EventEmitter = require('events');
const emitter = new EventEmitter();
// In Node.js, it works
emitter.on('event', async () => {
await someAsyncOperation();
});
emitter.emit('event');
console.log('Event emitted');
// Node.js: "Event emitted" → async operation
// Bun: May fall or be executed in a different orderError handling in asynchronous event handlers is different, which can lead to unhandled promise rejections.
Solution:
Always wrap asynchronous handlers:
emitter.on('event', (data) => {
(async () => {
try {
await someAsyncOperation(data);
} catch (error) {
console.error('Error in event handler:', error);
}
})();
});Problem #4: Differences in working with paths
Reality:
import path from 'path';
import { fileURLToPath } from 'url';
// Node.js + ES modules
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Bun behaves differently
console.log(__dirname); // May be undefined or incorrectSolution:
Use the built-in Bun features:
// The right way for Bun
const currentFile = import.meta.path;
const currentDir = import.meta.dir;
console.log('Current file:', currentFile);
console.log('Current directory:', currentDir);
Problem #5: Environment variables and dotenv
What is expected?
Bun automatically loads .env files, so dotenv is not needed.
Reality:
// In Node.js with dotenv
require('dotenv').config();
console.log(process.env.DATABASE_URL);
// In Bun
console.log(process.env.DATABASE_URL); // May not workBun loads .env, but the order of priorities is different, and some values may not be picked up.
Solution:
// Explicitly load the configuration
import { config } from 'dotenv';
config({ path: '.env' });
// Or use the Bun API
const env = Bun.env;
console.log(env.DATABASE_URL);Problem No. 6: Working with databases
Reality with PostgreSQL:
// Node.js + pg
import pg from 'pg';
const { Pool } = pg;
const pool = new Pool({
connectionString: process.env.DATABASE_URL
});
// Bun may not work reliably
const result = await pool.query('SELECT * FROM users');
// Sometimes it hangs or crashes with timeoutSolution:
Use native SQLite support in Bun or alternative drivers:
// Bun has built-in SQLite support
import { Database } from 'bun:sqlite';
const db = new Database('mydb.sqlite');
const query = db.query('SELECT * FROM users');
const users = query.all();
// For PostgreSQL, use postgres.js
import postgres from 'postgres';
const sql = postgres(process.env.DATABASE_URL);
const users = await sql`SELECT * FROM users`;Problem No. 7: Testing
Reality:
// Jest configuration does not work directly
// package.json
{
"scripts": {
"test": "jest"
}
}
// bun test runs its own test runnerBun has a built-in test runner, but it is not fully compatible with Jest.
Solution:
Rewrite the tests under Bun:
// test/example.test.ts
import { expect, test, describe } from 'bun:test';
describe('Math operations', () => {
test('addition', () => {
expect(2 + 2).toBe(4);
});
test('async operation', async () => {
const result = await fetchData();
expect(result).toBeDefined();
});
});Launch:
bun testProblem #8: Hot Reload and Watch Mode
Reality:
# Node.js with nodemon
nodemon server.js
# Bun
bun --watch server.ts
# It works, but may not restart when some files are changedWatch mode in Bun sometimes skips changes or restarts too often.
Solution:
Add explicit patterns:
// bunfig.toml
[watch]
ignore = ["node_modules", "dist", ".git"]
include = ["src/**/*.ts", "src/**/*.js"]Or use external tools:
npm install -D nodemon
nodemon --exec bun run server.tsProblem #9: Debugging
Reality:
Node.js has great debugging tools via Chrome DevTools or VS Code. In Bun, it works... differently.
# Node.js
node --inspect-brk server.js
# Bun
bun --inspect server.ts
# Does not always correctly show the call stackSolution:
Use console debugging and logging:
// Add detailed logging
console.log('Debug point 1:', { variable1, variable2 });
// Use formatting utilities
import util from 'util';
console.log(util.inspect(complexObject, { depth: null, colors: true }));
// Or Bun.inspect()
console.log(Bun.inspect(complexObject));Problem #10: Bundle size and tree-shaking
What is expected?
Bun should create optimized bundles with automatic tree-shaking.
Reality:
bun build ./src/index.ts --outdir ./dist
# Bundle may be larger than expectedTree-shaking doesn't always work effectively, especially with CommonJS modules.
Solution:
Use only ES modules and check the result:
// ❌ Bad
const lodash = require('lodash');
// ✅ Good
import { map, filter } from 'lodash-es';
// Assembly configuration
bun build ./src/index.ts \
--outdir ./dist \
--minify \
--splitting \
--target browserPractical recommendations for migration
Step 1: Start small
Do not transfer the entire project at once. Create a small test project:
mkdir bun-test && cd bun-test
bun initStep 2: Check dependencies
Create a list of all npm packages and check their compatibility:
# Set dependencies
bun install
# Run tests
bun testStep 3: Gradual migration
// Create a transition layer
// adapter.ts
export const runtime = {
isNode: typeof process !== 'undefined' && !process.versions.bun,
isBun: typeof process !== 'undefined' && !!process.versions.bun
};
export function getAdapter() {
if (runtime.isBun) {
return import('./adapters/bun');
}
return import('./adapters/node');
}Step 4: Testing in a production-like environment
# Dockerfile for Bun
FROM oven/bun:1 as base
WORKDIR /app
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile
COPY . .
RUN bun run build
CMD ["bun", "run", "start"]When NOT to switch to Bun
Do not go if:
The project uses many native modules - time saved on speed will be lost on searching for alternatives
Stability is critical — Bun is still young, bugs are encountered
The team is not ready for experiments - you will have to deal with new problems
Use specific Node.js features — streams, workers, special APIs
Need support for older versions — Bun does not support legacy code
When to try Bun
Switch if:
Create a new project — no old code baggage
Focus on development speed — quick installation of packages really speeds up the work
Use a modern stack — TypeScript, ES modules, modern libraries
Ready for experiments - you can spend time solving problems
Need a built-in bundler — you don't want to configure webpack/vite
A real example of migrating a simple Express application
Node.js version
// server.js
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.json({ message: 'Hello from Node.js' });
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});Bun version (what works)
// server.ts
import { serve } from 'bun';
serve({
port: 3000,
fetch(req) {
const url = new URL(req.url);
if (url.pathname === '/') {
return new Response(
JSON.stringify({ message: 'Hello from Bun' }),
{ headers: { 'Content-Type': 'application/json' } }
);
}
return new Response('Not Found', { status: 404 });
},
});
console.log('Server running on port 3000')Bun is an interesting technology, but it's still raw. Real problems with migration include:
Tips for beginners: Use Bun for new pet projects and experiments. For production applications, it is better to stay on Node.js if you do not have specific reasons to switch.
In the Codex we don't just tell the theory — you you get practical skills through real tasks and projects.
What you will get:
Structured courses - from basics to advanced topics
Practical tasks — consolidate knowledge with real examples
Step-by-step analysis — understand how and why the code works
Current technologies - study what is used in the industry
Need support and communication?
Join our active Telegram channel - already more than 2000 like-minded peoplewhich:
Discuss technologies and share experiences
Help each other with solving problems
Share useful materials and findings
Growing together as developers
Go to Kodik — start your programming journey with community support and quality materials! 🚀
