Bun é um runtime JavaScript ultra-rápido e gerenciador de pacotes escrito em Zig. Com o Bun 1.2, a compatibilidade com Node.js foi significativamente melhorada, acelerando ainda mais a adoção empresarial.
Principais Novos Recursos do Bun 1.2
Visão Geral
flowchart TB
subgraph Bun12["Bun 1.2"]
subgraph Compat["Compatibilidade Node.js"]
C1["Suporte completo a node:cluster"]
C2["Suporte a node:dgram (UDP)"]
C3["Suporte parcial a node:v8"]
C4["90%+ dos pacotes npm funcionando"]
end
subgraph Features["Novos Recursos"]
F1["Cliente S3 integrado"]
F2["API de cores Bun.color()"]
F3["Watch Mode melhorado"]
F4["Bundler HTML/CSS"]
end
subgraph Perf["Performance"]
P1["Servidor HTTP: 30% mais rápido"]
P2["Uso de memória: 20% reduzido"]
P3["Instalação de pacotes: 2x mais rápida"]
end
end
Melhoria na Compatibilidade com Node.js
Suporte a node:cluster
// cluster-server.ts - Servidor multiprocesso
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 workers de acordo com o número de núcleos da CPU
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died`);
// Reinício automático
cluster.fork();
});
// Receber mensagens dos workers
cluster.on('message', (worker, message) => {
console.log(`Message from worker ${worker.process.pid}:`, message);
});
} else {
// Processo 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 mensagem para o primary
process.send?.({ status: 'ready', pid: process.pid });
}
Suporte a 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}`);
// Retornar resposta
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
Simplificação de Operações S3
// Bun.s3 - Cliente S3 nativo
// Obtém credenciais automaticamente das variáveis de ambiente
// AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION
// Upload de arquivo
const file = Bun.file('./large-file.zip');
await Bun.s3.write('my-bucket/uploads/large-file.zip', file);
// Upload via streaming
const response = await fetch('https://example.com/large-video.mp4');
await Bun.s3.write('my-bucket/videos/video.mp4', response);
// Escrita de dados de texto
await Bun.s3.write(
'my-bucket/data/config.json',
JSON.stringify({ setting: 'value' }),
{ contentType: 'application/json' }
);
// Leitura de arquivo
const data = await Bun.s3.file('my-bucket/data/config.json').text();
const config = JSON.parse(data);
// Leitura como ArrayBuffer
const buffer = await Bun.s3.file('my-bucket/images/photo.jpg').arrayBuffer();
// Leitura via streaming
const stream = Bun.s3.file('my-bucket/large-data.csv').stream();
// Obter informações do arquivo
const s3File = Bun.s3.file('my-bucket/document.pdf');
console.log({
exists: await s3File.exists(),
size: await s3File.size,
lastModified: await s3File.lastModified,
});
// Exclusão de arquivo
await Bun.s3.delete('my-bucket/temp/old-file.txt');
// Geração de URL assinada
const signedUrl = await Bun.s3.presign('my-bucket/private/file.pdf', {
expiresIn: 3600, // 1 hora
method: 'GET',
});
// Listar objetos
for await (const object of Bun.s3.list('my-bucket/uploads/')) {
console.log(object.key, object.size, object.lastModified);
}
Servidor de Arquivos com 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); // Remove a / inicial
if (req.method === 'GET') {
// Streaming direto do 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 do corpo da requisição para o 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()
Nova API para Processamento de Cores
// Bun.color() - Processamento de cores de alta performance
// Análise de strings de cores CSS
const red = Bun.color('red');
console.log(red); // { r: 255, g: 0, b: 0, a: 1 }
// Conversão de HEX
const hex = Bun.color('#3498db');
console.log(hex); // { r: 52, g: 152, b: 219, a: 1 }
// Conversão de RGB
const rgb = Bun.color('rgb(100, 150, 200)');
console.log(rgb); // { r: 100, g: 150, b: 200, a: 1 }
// Conversão de HSL
const hsl = Bun.color('hsl(210, 60%, 50%)');
console.log(hsl); // { r: ..., g: ..., b: ..., a: 1 }
// Saída em formato de array
const [r, g, b, a] = Bun.color('#ff6b6b', 'array');
console.log(r, g, b, a); // 255, 107, 107, 1
// Conversão para string HEX
const hexString = Bun.color({ r: 255, g: 100, b: 50, a: 1 }, 'hex');
console.log(hexString); // '#ff6432'
// Conversão para 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)'
// Manipulação de cores
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)); // Clarear
console.log(darken('#3498db', 30)); // Escurecer
Watch Mode Melhorado
Monitoramento de Arquivos Aprimorado
// Servidor de desenvolvimento com bun --watch
// package.json
{
"scripts": {
"dev": "bun --watch src/index.ts",
"dev:hot": "bun --hot src/server.ts"
}
}
// --watch: Reinício do processo
// --hot: Hot reload (mantém estado)
// hot-reload-server.ts
import { serve } from 'bun';
// Estado global (mantido 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');
Monitoramento de Arquivos Programático
// Bun.watch() - API de monitoramento de arquivos
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') {
// Processamento ao modificar arquivo
await runTests(event.path);
}
if (event.type === 'delete') {
// Processamento ao excluir arquivo
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
Simplificação de Build 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:
// - Detecta automaticamente CSS/JS no HTML
// - Compila automaticamente TypeScript/TSX
// - Faz bundle e otimização de CSS
// - Resolve dependências
// 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)`);
}
Melhorias de Performance
Comparação de Benchmarks
Performance do Servidor HTTP (req/sec)
| Runtime | Hello World | JSON API | Servir Arquivos |
|---|---|---|---|
| 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 |
Tempo de Instalação de Pacotes (node_modules)
| Ferramenta | Limpo | Com Cache |
|---|---|---|
| bun install | 2.1s | 0.3s |
| npm install | 15.2s | 8.5s |
| yarn install | 12.8s | 4.2s |
| pnpm install | 8.5s | 1.8s |
Exemplo de Uso de API Otimizada
// Servidor JSON de alta performance
import { serve } from 'bun';
const users = new Map<string, User>();
serve({
port: 3000,
async fetch(req) {
const url = new URL(req.url);
// Bun.router() - Roteamento de alta velocidade (recurso futuro)
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 });
},
});
Recursos Nativos do Bun
Bun.sql (Operações SQLite de Alta Performance)
// Bun.sql - Driver SQLite integrado
import { Database } from 'bun:sqlite';
const db = new Database(':memory:');
// Criar tabela
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
)
`);
// Prepared statements
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
`);
// Inserção de dados
insertUser.run({ $name: 'Alice', $email: 'alice@example.com' });
insertUser.run({ $name: 'Bob', $email: 'bob@example.com' });
// Consulta de dados
const user = getUser.get({ $id: 1 });
console.log(user); // { id: 1, name: 'Alice', ... }
const users = getAllUsers.all();
console.log(users);
// Transação
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 Senhas)
// Bun.password - Hash seguro de senhas
// Gerar hash de senha
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$...
// Verificação de senha
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 e Timers
// Bun.sleep - Sleep de alta precisão
// Especificar milissegundos
await Bun.sleep(1000); // Aguardar 1 segundo
// Timer nomeado (cancelável)
const timer = Bun.sleep(5000);
setTimeout(() => timer.cancel(), 2000); // Cancelar após 2 segundos
try {
await timer;
} catch (e) {
console.log('Timer was cancelled');
}
// sleepSync (versão síncrona)
Bun.sleepSync(100); // Aguardar 100ms de forma síncrona
Guia de Migração
Migração do Node.js
// package.json
{
"scripts": {
// Node.js
"dev:node": "node --watch src/index.js",
"build:node": "tsc && node dist/index.js",
// Bun (execução direta de TypeScript)
"dev": "bun --watch src/index.ts",
"build": "bun build src/index.ts --outdir=dist",
"start": "bun dist/index.js"
}
}
// Verificação de compatibilidade
// bun pm untrusted - Verificar pacotes não confiáveis
// bun pm verify - Verificar integridade dos pacotes
// Problemas comuns de compatibilidade e soluções:
// 1. __dirname / __filename
// Node.js: Disponível globalmente
// Bun: Em ESM, usar import.meta
const __dirname = import.meta.dir;
const __filename = import.meta.file;
// 2. require() em ESM
// No Bun, usar import.meta.require()
const pkg = import.meta.require('./package.json');
// 3. Native addons
// Alguns módulos nativos são incompatíveis
// Alternativa: API nativa do Bun ou WASM
Exemplo de Migração Express para 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 otimizado 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}`);
Resumo
Bun 1.2 alcançou um nível prático como alternativa ao Node.js.
Pontos Fortes do Bun 1.2
| Característica | Benefício |
|---|---|
| Inicialização ultra-rápida | Aceleração do ciclo de desenvolvimento |
| Execução direta de TypeScript | Sem necessidade de build |
| Tudo em um | Runtime+Bundler+PM |
| Compatível com Node.js | Reutilização de código existente |
Critérios de Adoção
- Novos projetos: Adoção recomendada
- Projetos Node.js existentes: Migrar após testes de compatibilidade
- Enterprise: Recomenda-se adoção gradual
Bun está se tornando o novo padrão para desenvolvimento JavaScript/TypeScript.