Skip to content
Search
See every article
Bun

Bun: runtime, gestor de paquetes y test runner en un binario

Bun es un único binario que corre TypeScript sin transpilar aparte, instala paquetes de npm hasta 30 veces más rápido y levanta servidores HTTP con las mismas APIs Request y Response del navegador. Sirve para dejar de armar tu stack con cuatro herramientas distintas.

Un binario reemplaza cuatro herramientas separadas

Bun no es solo un runtime alternativo a Node: el mismo comando bun hace de runtime, gestor de paquetes, test runner y bundler. Eso significa que el mismo binario que ejecuta tu servidor también instala dependencias, corre tests con API compatible con Jest y bundlea para el browser. La ventaja concreta es menos piezas que versionar y actualizar en tu proyecto: no hay ts-node, no hay Jest aparte, no hay webpack o esbuild como dependencia extra.

bash
bun run start # corre el script "start" del package.json
bun install <pkg> # instala una dependencia
bun build ./index.tsx # bundlea un proyecto para el browser
bun test # corre los tests
bunx cowsay 'Hello, world!' # ejecuta un paquete sin instalarlo

TypeScript y JSX corren directo, sin paso de compilación previo

Bun está escrito en Rust y usa JavaScriptCore (el motor de Safari) en vez de V8, lo que reduce el tiempo de arranque y el uso de memoria frente a Node. La consecuencia práctica es que podés ejecutar un archivo .ts o .tsx tal cual, sin tsc ni un loader configurado: Bun transpila internamente antes de correr el código. Esto elimina una capa entera de configuración que en un proyecto Node típico vive en tsconfig.json más un bundler.

typescript
// index.ts
const greet = (name: string): string =>
`Hello, ${name}, from Bun ${Bun.version}!`;
​
console.log(greet("AI Talks"));
​
// $ bun run index.ts
// Hello, AI Talks, from Bun 1.x.x!

Bun.serve() usa Request y Response, no una API propia

El servidor HTTP nativo de Bun se expone como Bun.serve() y recibe un handler fetch que trabaja con los objetos Request y Response estándar de la web, los mismos que fetch() en el browser. Esto importa porque el mismo handler es portable a otros runtimes que comparten ese contrato, sin reescribir nada. Corriendo el archivo con la flag --hot, Bun recarga el módulo en cada guardado pero preserva el estado en memoria, así una conexión abierta o un caché no se pierden en cada edición.

typescript
// src/server.ts
const server = Bun.serve({
port: 3000,
hostname: "0.0.0.0",
fetch(req) {
const url = new URL(req.url);
if (url.pathname === "/") {
return new Response("Bun is alive on " + Bun.version);
}
if (url.pathname === "/health") {
return Response.json({ status: "ok", uptime: process.uptime() });
}
return new Response("Not found", { status: 404 });
},
error(err) {
console.error(err);
return new Response("Internal Server Error", { status: 500 });
},
});
​
console.log(`Listening on http://${server.hostname}:${server.port}`);

bun:sqlite trae la base de datos embebida en el binario

Bun incluye un binding a SQLite bajo el módulo bun:sqlite, sin instalar nada ni compilar un módulo nativo como better-sqlite3. Esto sirve para tener persistencia real desde el primer minuto de un proyecto chico, sin levantar un servidor de base de datos aparte. La opción strict: true, más nueva en Bun, tira error si mandás un parámetro del tipo equivocado en vez de coaccionarlo en silencio, y el modo WAL evita que las escrituras bloqueen las lecturas en un servidor con tráfico concurrente.

typescript
// src/db.ts
import { Database } from "bun:sqlite";
​
const dbPath = process.env.DB_PATH ?? "data.db";
export const db = new Database(dbPath, { create: true, strict: true });
​
db.exec("PRAGMA journal_mode = WAL;");
db.exec("PRAGMA foreign_keys = ON;");
db.exec(`
CREATE TABLE IF NOT EXISTS articles (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
body TEXT NOT NULL,
published INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
`);

bun install con caché global y control explícito de scripts

El gestor de paquetes de Bun resuelve dependencias contra un caché global en ~/.bun/install/cache y usa hard links en Linux y macOS, así que instalar el mismo paquete en dos proyectos distintos casi no ocupa disco extra. Para reproducibilidad en CI existe bun install --frozen-lockfile, que falla si el lockfile no coincide exactamente con lo instalado. Por seguridad, Bun solo corre scripts postinstall de paquetes que estén listados explícitamente en trustedDependencies dentro del package.json, así un paquete nativo no ejecuta código arbitrario en la instalación sin que vos lo hayas autorizado.

bash
bun add hono zod
bun add -d @types/node typescript
bun pm ls
bun install --frozen-lockfile

What you can build with this

Three projects you can finish in an afternoon, each ending in something that runs.

  1. API de tareas con persistencia en SQLite, sin ORM ni Docker

    Un servidor Bun.serve() con rutas para crear y listar tareas guardadas en una tabla de bun:sqlite.

    Why it's worth it — Para probar una idea con persistencia real sin instalar Postgres, un driver nativo o levantar un contenedor.

  2. Servidor con hot reload que no pierde estado en memoria

    Un server Bun.serve() con un contador o caché en memoria, corrido con la flag --hot mientras editás el handler.

    Why it's worth it — Para iterar sobre la lógica de un servidor sin que cada guardado reinicie las conexiones o borre el estado acumulado.

  3. Migrar un proyecto Node a Bun y medir la instalación con datos propios

    Reemplazar npm install por bun install en un repo existente y comparar tiempos con bun pm ls y bun install --frozen-lockfile.

    Why it's worth it — Para decidir si conviene mover un proyecto real a Bun con números concretos, no con la promesa genérica de que es más rápido.

What you take away

  • Bun junta runtime, gestor de paquetes, test runner y bundler en un solo binario: menos herramientas externas que mantener en tu proyecto.

  • Bun.serve() trabaja con Request y Response estándar de la web, así que el mismo handler es portable a otros runtimes sin reescribirlo.

  • bun:sqlite te da persistencia embebida sin instalar módulos nativos, útil para prototipos que necesitan guardar datos de verdad desde el día uno.

  • El caché global y trustedDependencies hacen que bun install sea rápido y explícito sobre qué scripts postinstall se ejecutan, en vez de correrlos todos en silencio.

This article was written from the official documentation linked below. The code comes from there, not from a model's memory.

Where this came from