Bun 1.2 a Fondo: SQLite y S3 Integrados - ¿Por fin puede reemplazar a Node.js?
Bun 1.2 a Fondo: SQLite y S3 Integrados - ¿Por fin puede reemplazar a Node.js?
¿Qué te viene a la mente cuando piensas en Bun? Instalación rápida, ejecución rápida, soporte nativo de TypeScript. Pero para la mayoría de nosotros, era "genial para proyectos personales, pero no listo para producción".
Bun 1.2 cambia esa conversación.
Lanzado en enero de 2025, Bun 1.2 no es solo otra actualización menor. Viene con SQLite integrado, cliente S3 nativo, soporte de Postgres, y una compatibilidad con Node.js que finalmente alcanza el 96% de la suite de tests. Sin paquetes npm. Sin configuración. Solo importa y usa.
En este análisis profundo, exploraremos qué ofrece Bun 1.2, ejecutaremos benchmarks reales y determinaremos si finalmente es momento de considerar Bun para tu próximo proyecto en producción.
¿Qué hay de nuevo en Bun 1.2?
Cortemos el hype y veamos qué entrega realmente:
1. Base de datos SQLite integrada
SQLite ahora es ciudadano de primera clase en Bun. Sin instalación:
import { Database } from "bun:sqlite"; const db = new Database("myapp.db"); // Crear tablas db.run(` CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT UNIQUE NOT NULL, name TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) `); // Insertar datos const insert = db.prepare("INSERT INTO users (email, name) VALUES (?, ?)"); insert.run("[email protected]", "John Doe"); // Consultar con tipos seguros interface User { id: number; email: string; name: string; created_at: string; } const users = db.prepare("SELECT * FROM users").all() as User[]; console.log(users);
Esto no es un wrapper de better-sqlite3. Es una implementación nativa significativamente más rápida:
| Operación | better-sqlite3 (Node.js) | Bun SQLite | Diferencia |
|---|---|---|---|
| INSERT 1M filas | 4.2s | 1.8s | 2.3x más rápido |
| SELECT 100K filas | 320ms | 140ms | 2.3x más rápido |
| Commit transacción | 12ms | 5ms | 2.4x más rápido |
Las ganancias de rendimiento vienen de la integración de Bun con JavaScriptCore y evitar el overhead de N-API que tienen los addons de Node.js.
2. Cliente S3 nativo
Almacenamiento en la nube sin dependencias. El cliente S3 de Bun funciona con AWS S3, R2, MinIO y cualquier servicio compatible:
import { S3Client } from "bun"; const s3 = new S3Client({ endpoint: "https://s3.amazonaws.com", region: "us-east-1", accessKeyId: process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, }); // Subir archivo const file = Bun.file("./large-video.mp4"); await s3.write("my-bucket/videos/intro.mp4", file); // Descargar archivo const downloaded = await s3.file("my-bucket/videos/intro.mp4"); await Bun.write("./downloaded.mp4", downloaded); // Streaming de archivos grandes const stream = s3.file("my-bucket/data/huge.csv").stream(); for await (const chunk of stream) { // Procesar chunk sin cargar todo en memoria }
El cliente S3 maneja automáticamente uploads multipart para archivos grandes y soporta URLs pre-firmadas. Compara con el AWS SDK:
// AWS SDK v3 - La forma antigua import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3"; import fs from "fs"; const client = new S3Client({ region: "us-east-1" }); const fileStream = fs.createReadStream("./large-video.mp4"); await client.send(new PutObjectCommand({ Bucket: "my-bucket", Key: "videos/intro.mp4", Body: fileStream, })); // Bun - La forma nueva const s3 = new S3Client({ /* config */ }); await s3.write("my-bucket/videos/intro.mp4", Bun.file("./large-video.mp4"));
La superficie de API es dramáticamente más pequeña mientras proporciona la misma funcionalidad.
3. Soporte Postgres integrado
Postgres se une a SQLite como opción de base de datos integrada:
import { sql } from "bun"; // Conexión desde variable de entorno (DATABASE_URL) const users = await sql`SELECT * FROM users WHERE active = ${true}`; // O conexión explícita import { SQL } from "bun"; const db = new SQL({ hostname: "localhost", port: 5432, database: "myapp", username: "postgres", password: "secret", }); // Consultas parametrizadas son automáticas const email = "[email protected]"; const user = await db`SELECT * FROM users WHERE email = ${email}`; // Transacciones await db.begin(async (tx) => { await tx`UPDATE accounts SET balance = balance - 100 WHERE id = 1`; await tx`UPDATE accounts SET balance = balance + 100 WHERE id = 2`; });
El enfoque de template literal previene SQL injection por diseño y proporciona excelente DX.
4. Compatibilidad Node.js: 96% y subiendo
El mayor bloqueador para la adopción de Bun ha sido la compatibilidad. Bun 1.2 ahora pasa:
- 96% de la suite de tests de Node.js
- 100% de tests de
node:fs - 100% de tests de
node:path - 99% de tests de
node:crypto - 98% de tests de
node:http
Esto significa que la mayoría de paquetes npm "simplemente funcionan". Probamos varios paquetes populares:
| Paquete | Estado | Notas |
|---|---|---|
| Express | ✅ Funciona | Compatibilidad total |
| Fastify | ✅ Funciona | Compatibilidad total |
| Prisma | ✅ Funciona | Desde Bun 1.1 |
| Next.js | ⚠️ Parcial | Dev server funciona, algunos edge cases |
| NestJS | ✅ Funciona | Compatibilidad total |
| Socket.io | ✅ Funciona | Compatibilidad total |
5. Soporte Windows (¡Por fin!)
Bun ahora corre nativamente en Windows sin WSL:
powershell -c "irm bun.sh/install.ps1 | iex"
El rendimiento en Windows es comparable a Linux/macOS, lo cual no era el caso con versiones anteriores.
Benchmark real: Construyendo un servidor API
Construyamos la misma API con Node.js y Bun para ver diferencias reales de rendimiento.
El test: API CRUD de usuarios con SQLite
Implementación Bun:
// server.ts (Bun) import { Database } from "bun:sqlite"; const db = new Database(":memory:"); db.run(` CREATE TABLE users ( id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT UNIQUE, name TEXT ) `); // Datos semilla const insert = db.prepare("INSERT INTO users (email, name) VALUES (?, ?)"); for (let i = 0; i < 1000; i++) { insert.run(`user${i}@test.com`, `User ${i}`); } const server = Bun.serve({ port: 3000, async fetch(req) { const url = new URL(req.url); if (url.pathname === "/users" && req.method === "GET") { const users = db.prepare("SELECT * FROM users LIMIT 100").all(); return Response.json(users); } if (url.pathname === "/users" && req.method === "POST") { const body = await req.json(); const result = db.prepare( "INSERT INTO users (email, name) VALUES (?, ?) RETURNING *" ).get(body.email, body.name); return Response.json(result, { status: 201 }); } return new Response("Not Found", { status: 404 }); }, }); console.log(`Server running at http://localhost:${server.port}`);
Implementación Node.js:
// server.mjs (Node.js con better-sqlite3) import Database from "better-sqlite3"; import { createServer } from "http"; const db = new Database(":memory:"); db.exec(` CREATE TABLE users ( id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT UNIQUE, name TEXT ) `); const insert = db.prepare("INSERT INTO users (email, name) VALUES (?, ?)"); for (let i = 0; i < 1000; i++) { insert.run(`user${i}@test.com`, `User ${i}`); } const server = createServer(async (req, res) => { const url = new URL(req.url, `http://${req.headers.host}`); if (url.pathname === "/users" && req.method === "GET") { const users = db.prepare("SELECT * FROM users LIMIT 100").all(); res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify(users)); return; } if (url.pathname === "/users" && req.method === "POST") { let body = ""; for await (const chunk of req) body += chunk; const data = JSON.parse(body); const result = db.prepare( "INSERT INTO users (email, name) VALUES (?, ?) RETURNING *" ).get(data.email, data.name); res.writeHead(201, { "Content-Type": "application/json" }); res.end(JSON.stringify(result)); return; } res.writeHead(404); res.end("Not Found"); }); server.listen(3000, () => console.log("Server running at http://localhost:3000"));
Resultados del benchmark (M2 MacBook Pro, 10,000 requests)
GET /users (leer 100 filas):
| Runtime | Requests/seg | Latencia Prom | Latencia P99 |
|---|---|---|---|
| Node.js 22 | 12,450 | 7.8ms | 15ms |
| Bun 1.2 | 28,900 | 3.2ms | 8ms |
| Diferencia | 2.3x más rápido | 2.4x más rápido | 1.9x más rápido |
POST /users (insertar + retornar):
| Runtime | Requests/seg | Latencia Prom | Latencia P99 |
|---|---|---|---|
| Node.js 22 | 8,200 | 11.5ms | 22ms |
| Bun 1.2 | 19,400 | 4.8ms | 12ms |
| Diferencia | 2.4x más rápido | 2.4x más rápido | 1.8x más rápido |
Los resultados son consistentes: Bun es aproximadamente 2-2.5x más rápido para operaciones de API respaldadas por base de datos.
Comparación de tiempo de inicio
El cold start importa para serverless:
| Runtime | Tiempo de inicio | Memoria al inicio |
|---|---|---|
| Node.js 22 | 45ms | 52MB |
| Bun 1.2 | 8ms | 28MB |
| Diferencia | 5.6x más rápido | 46% menos |
Para funciones serverless, esta diferencia es significativa.
¿Cuándo deberías usar Bun en producción?
Basado en nuestras pruebas, aquí hay una evaluación realista:
✅ Buenos candidatos para Bun
-
Nuevos proyectos con dependencias simples
- APIs con SQLite/Postgres
- Workers en background
- Herramientas CLI
- Microservicios
-
Aplicaciones críticas en rendimiento
- APIs de alto throughput
- Aplicaciones en tiempo real
- Funciones edge
-
Funciones serverless
- Cuando importa el cold start
- Cuando los costos de memoria son una preocupación
⚠️ Proceder con precaución
-
Aplicaciones grandes de Next.js
- Funciona en la mayoría de casos, pero existen edge cases
- Probar exhaustivamente antes de deployar
-
Aplicaciones con addons nativos de Node.js
- Algunos addons nativos pueden no funcionar
- Verificar compatibilidad primero
-
Codebases legacy con suposiciones profundas de Node.js
- El esfuerzo de migración puede no valer la pena
❌ No recomendado (aún)
-
Aplicaciones Electron
- Sin soporte de Electron
-
Aplicaciones que requieren
node:vm- Soporte VM limitado
-
Sistemas financieros de misión crítica
- Esperar más pruebas en producción
Guía de migración: Moviéndose de Node.js a Bun
Si decides probar Bun, aquí está cómo migrar:
Paso 1: Instalar Bun
curl -fsSL https://bun.sh/install | bash
Paso 2: Verificar compatibilidad
# Ejecutar tests existentes con Bun bun test # Buscar problemas bun run your-script.ts
Paso 3: Actualizar scripts de package.json
{ "scripts": { "dev": "bun run --watch src/index.ts", "start": "bun run src/index.ts", "test": "bun test" } }
Paso 4: Eliminar dependencias innecesarias
Con los integrados de Bun, a menudo puedes eliminar:
better-sqlite3→ Usarbun:sqlite@aws-sdk/client-s3→ UsarBun.S3Clientpg→ UsarbunSQLdotenv→ Bun carga.envautomáticamentets-node/tsx→ No necesarios
Paso 5: Actualizar Dockerfile
FROM oven/bun:1.2 WORKDIR /app COPY package.json bun.lockb ./ RUN bun install --frozen-lockfile COPY . . EXPOSE 3000 CMD ["bun", "run", "src/index.ts"]
El elefante en la habitación: ¿Deberías confiar en él?
Bun es desarrollado por Oven, una startup. La pregunta natural: ¿qué pasa si la empresa quiebra?
Argumentos a favor de confiar:
- Open source (licencia MIT)
- Gran comunidad (70k+ estrellas en GitHub)
- Ex-miembros del equipo WebKit/Safari involucrados
- Adopción empresarial creciente
Argumentos para precaución:
- Node.js tiene 15+ años de batalla
- Alta dependencia de personas clave (si Jarred Sumner se va, impacto mayor)
- Aún existen algunos edge cases
Nuestra recomendación: Empieza con proyectos nuevos y no críticos. Si todo va bien, expande gradualmente.
Conclusión: ¿El punto de inflexión?
Bun 1.2 representa un hito significativo:
- Rendimiento: 2-3x más rápido es real, no marketing
- DX: Menos dependencias, APIs más simples
- Compatibilidad: 96% de la suite de tests de Node.js
- Características: Herramientas integradas que requerirían 5+ paquetes npm
¿Está Bun listo para reemplazar Node.js? Para nuevos proyectos: cada vez más sí. Para sistemas en producción existentes: migra cuidadosa e incrementalmente.
Una cosa está clara: ignorar Bun ya no es una opción. Pruébalo en tu próximo proyecto. Podrías sorprenderte.
# Instalar y ejecutar tu primer proyecto Bun curl -fsSL https://bun.sh/install | bash bun init bun run index.ts
Bienvenido al futuro de los runtimes de JavaScript.