Bun es un runtime de JavaScript ultrarrápido y gestor de paquetes escrito en el lenguaje Zig. Con Bun 1.2, la compatibilidad con Node.js ha mejorado significativamente, acelerando aún más su adopción empresarial.
Principales novedades de Bun 1.2
Resumen
flowchart TB
subgraph Bun12["Bun 1.2"]
subgraph Compat["Compatibilidad con Node.js"]
C1["Soporte completo para node:cluster"]
C2["Soporte para node:dgram (UDP)"]
C3["Soporte parcial para node:v8"]
C4["Más del 90% de paquetes npm funcionan"]
end
subgraph Features["Nuevas funciones"]
F1["Cliente S3 integrado"]
F2["API Bun.color() para procesamiento de colores"]
F3["Watch Mode mejorado"]
F4["Bundler HTML/CSS"]
end
subgraph Perf["Rendimiento"]
P1["Servidor HTTP: 30% más rápido"]
P2["Uso de memoria: 20% menos"]
P3["Instalación de paquetes: 2x más rápida"]
end
end
Mejoras en compatibilidad con Node.js
Soporte para node:cluster
// cluster-server.ts - Servidor multiproceso
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 de workers según el número de núcleos de CPU
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died`);
// Reinicio automático
cluster.fork();
});
// Recepción de mensajes de los workers
cluster.on('message', (worker, message) => {
console.log(`Message from worker ${worker.process.pid}:`, message);
});
} else {
// Proceso worker
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}`);
// Enviar mensaje al primario
process.send?.({ status: 'ready', pid: process.pid });
}
Soporte para node:dgram (UDP)
// 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}`);
// Devolver respuesta
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();
});
Cliente S3 integrado
Simplificación de operaciones S3
// Bun.s3 - Cliente S3 nativo
// Obtención automática de credenciales desde variables de entorno
// AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION
// Subida de archivos
const file = Bun.file('./large-file.zip');
await Bun.s3.write('my-bucket/uploads/large-file.zip', file);
// Subida en streaming
const response = await fetch('https://example.com/large-video.mp4');
await Bun.s3.write('my-bucket/videos/video.mp4', response);
// Escritura de datos de texto
await Bun.s3.write(
'my-bucket/data/config.json',
JSON.stringify({ setting: 'value' }),
{ contentType: 'application/json' }
);
// Lectura de archivos
const data = await Bun.s3.file('my-bucket/data/config.json').text();
const config = JSON.parse(data);
// Lectura como ArrayBuffer
const buffer = await Bun.s3.file('my-bucket/images/photo.jpg').arrayBuffer();
// Lectura en streaming
const stream = Bun.s3.file('my-bucket/large-data.csv').stream();
// Obtención de información del archivo
const s3File = Bun.s3.file('my-bucket/document.pdf');
console.log({
exists: await s3File.exists(),
size: await s3File.size,
lastModified: await s3File.lastModified,
});
// Eliminación de archivos
await Bun.s3.delete('my-bucket/temp/old-file.txt');
// Generación de URL firmada
const signedUrl = await Bun.s3.presign('my-bucket/private/file.pdf', {
expiresIn: 3600, // 1 hora
method: 'GET',
});
// Obtención de lista
for await (const object of Bun.s3.list('my-bucket/uploads/')) {
console.log(object.key, object.size, object.lastModified);
}
Servidor de archivos usando S3
// 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); // Eliminar / inicial
if (req.method === 'GET') {
// Streaming directo desde 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') {
// Streaming del cuerpo de la petición a 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 });
},
});
API Bun.color()
Nueva API para procesamiento de colores
// Bun.color() - Procesamiento de colores de alto rendimiento
// Análisis de cadenas de color CSS
const red = Bun.color('red');
console.log(red); // { r: 255, g: 0, b: 0, a: 1 }
// Conversión desde HEX
const hex = Bun.color('#3498db');
console.log(hex); // { r: 52, g: 152, b: 219, a: 1 }
// Conversión desde RGB
const rgb = Bun.color('rgb(100, 150, 200)');
console.log(rgb); // { r: 100, g: 150, b: 200, a: 1 }
// Conversión desde HSL
const hsl = Bun.color('hsl(210, 60%, 50%)');
console.log(hsl); // { r: ..., g: ..., b: ..., a: 1 }
// Salida en formato array
const [r, g, b, a] = Bun.color('#ff6b6b', 'array');
console.log(r, g, b, a); // 255, 107, 107, 1
// Conversión a cadena HEX
const hexString = Bun.color({ r: 255, g: 100, b: 50, a: 1 }, 'hex');
console.log(hexString); // '#ff6432'
// Conversión a formato CSS
const cssRgb = Bun.color({ r: 255, g: 100, b: 50, a: 0.5 }, 'css');
console.log(cssRgb); // 'rgba(255, 100, 50, 0.5)'
// Operaciones con colores
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)); // Más claro
console.log(darken('#3498db', 30)); // Más oscuro
Watch Mode mejorado
Mejoras en la vigilancia de archivos
// Servidor de desarrollo con bun --watch
// package.json
{
"scripts": {
"dev": "bun --watch src/index.ts",
"dev:hot": "bun --hot src/server.ts"
}
}
// --watch: Reinicio del proceso
// --hot: Hot reload (mantiene el estado)
// hot-reload-server.ts
import { serve } from 'bun';
// Estado global (se mantiene durante 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');
Vigilancia de archivos programática
// Bun.watch() - API de vigilancia de archivos
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') {
// Procesamiento al cambiar archivos
await runTests(event.path);
}
if (event.type === 'delete') {
// Procesamiento al eliminar archivos
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;
}
}
Bundler HTML
Simplificación del build de frontend
// bun build --html
// Entrada: 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>
// Comando de build
// bun build ./index.html --outdir=./dist
// Resultado:
// - Detección automática de CSS/JS en HTML
// - Compilación automática de TypeScript/TSX
// - Bundling y optimización de CSS
// - Resolución de dependencias
// Build HTML programático
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)`);
}
Mejoras de rendimiento
Comparación de benchmarks
Rendimiento del servidor HTTP (req/seg)
| Runtime | Hello World | JSON API | Servicio de archivos |
|---|---|---|---|
| Bun 1.2 | 250,000 | 180,000 | 150,000 |
| Bun 1.1 | 190,000 | 140,000 | 120,000 |
| Node.js 20 | 75,000 | 55,000 | 45,000 |
| Deno 1.40 | 95,000 | 70,000 | 60,000 |
Tiempo de instalación de paquetes (node_modules)
| Herramienta | Instalación limpia | Con caché |
|---|---|---|
| bun install | 2.1s | 0.3s |
| npm install | 15.2s | 8.5s |
| yarn install | 12.8s | 4.2s |
| pnpm install | 8.5s | 1.8s |
Ejemplo de uso de API optimizado
// Servidor JSON de alto rendimiento
import { serve } from 'bun';
const users = new Map<string, User>();
serve({
port: 3000,
async fetch(req) {
const url = new URL(req.url);
// Bun.router() - Routing de alto rendimiento (función futura)
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 });
},
});
Funciones nativas de Bun
Bun.sql (Operaciones SQLite de alto rendimiento)
// Bun.sql - Driver SQLite integrado
import { Database } from 'bun:sqlite';
const db = new Database(':memory:');
// Creación de tabla
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
)
`);
// Sentencias preparadas
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
`);
// Inserción de datos
insertUser.run({ $name: 'Alice', $email: 'alice@example.com' });
insertUser.run({ $name: 'Bob', $email: 'bob@example.com' });
// Obtención de datos
const user = getUser.get({ $id: 1 });
console.log(user); // { id: 1, name: 'Alice', ... }
const users = getAllUsers.all();
console.log(users);
// Transacciones
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 (Hash de contraseñas)
// Bun.password - Hash seguro de contraseñas
// Hashear contraseña
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$...
// Verificar contraseña
const isValid = await Bun.password.verify(
'my-secure-password',
hash
);
console.log(isValid); // true
// Uso de bcrypt
const bcryptHash = await Bun.password.hash('password123', {
algorithm: 'bcrypt',
cost: 12,
});
const bcryptValid = await Bun.password.verify('password123', bcryptHash);
Bun.sleep y temporizadores
// Bun.sleep - Sleep de alta precisión
// Especificación en milisegundos
await Bun.sleep(1000); // Esperar 1 segundo
// Temporizador con nombre (cancelable)
const timer = Bun.sleep(5000);
setTimeout(() => timer.cancel(), 2000); // Cancelar después de 2 segundos
try {
await timer;
} catch (e) {
console.log('Timer was cancelled');
}
// sleepSync (versión síncrona)
Bun.sleepSync(100); // Espera síncrona de 100ms
Guía de migración
Migración desde Node.js
// package.json
{
"scripts": {
// Node.js
"dev:node": "node --watch src/index.js",
"build:node": "tsc && node dist/index.js",
// Bun (ejecución directa de TypeScript)
"dev": "bun --watch src/index.ts",
"build": "bun build src/index.ts --outdir=dist",
"start": "bun dist/index.js"
}
}
// Verificación de compatibilidad
// bun pm untrusted - Verificar paquetes no confiables
// bun pm verify - Verificar integridad de paquetes
// Problemas comunes de compatibilidad y soluciones:
// 1. __dirname / __filename
// Node.js: disponible globalmente
// Bun: usar import.meta en ESM
const __dirname = import.meta.dir;
const __filename = import.meta.file;
// 2. require() en ESM
// En Bun usar import.meta.require()
const pkg = import.meta.require('./package.json');
// 3. Native addons
// Algunos módulos nativos son incompatibles
// Alternativa: API nativa de Bun o WASM
Ejemplo de migración Express a Elysia
// 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 (Framework optimizado para Bun)
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}`);
Resumen
Bun 1.2 ha alcanzado un nivel práctico como alternativa a Node.js.
Fortalezas de Bun 1.2
| Característica | Beneficio |
|---|---|
| Inicio ultrarrápido | Acelera el ciclo de desarrollo |
| Ejecución directa de TypeScript | Sin paso de build |
| Todo en uno | Runtime + Bundler + PM |
| Compatibilidad con Node.js | Reutilización de código existente |
Criterios de adopción
- Nuevos proyectos: Se puede adoptar activamente
- Proyectos Node.js existentes: Migrar después de pruebas de compatibilidad
- Enterprise: Se recomienda adopción gradual
Bun se está convirtiendo en el nuevo estándar del desarrollo JavaScript/TypeScript.