Bun 1.2 Análise Completa: SQLite e S3 Integrados - Finalmente pode substituir o Node.js?
Bun 1.2 Análise Completa: SQLite e S3 Integrados - Finalmente pode substituir o Node.js?
O que vem na sua cabeça quando pensa em Bun? Instalação rápida, execução rápida, suporte nativo a TypeScript. Mas pra maioria de nós, era "legal pra projetos pessoais, mas produção ainda não".
O Bun 1.2 muda essa conversa.
Lançado em janeiro de 2025, o Bun 1.2 não é só mais uma atualização menor. Vem com SQLite integrado, cliente S3 nativo, suporte a Postgres, e compatibilidade com Node.js que finalmente chegou a 96% da suite de testes. Sem pacotes npm. Sem configuração. Só importa e usa.
Nessa análise profunda, vamos explorar o que o Bun 1.2 realmente oferece, rodar benchmarks reais e determinar se finalmente é hora de considerar o Bun pro seu próximo projeto em produção.
O que tem de novo no Bun 1.2?
Vamos cortar o hype e ver o que realmente entrega:
1. Banco de dados SQLite integrado
SQLite agora é cidadão de primeira classe no Bun. Sem instalação:
import { Database } from "bun:sqlite"; const db = new Database("myapp.db"); // Criar tabelas 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 ) `); // Inserir dados const insert = db.prepare("INSERT INTO users (email, name) VALUES (?, ?)"); insert.run("[email protected]", "John Doe"); // Consultar com tipagem interface User { id: number; email: string; name: string; created_at: string; } const users = db.prepare("SELECT * FROM users").all() as User[]; console.log(users);
Isso não é um wrapper do better-sqlite3. É uma implementação nativa significativamente mais rápida:
| Operação | better-sqlite3 (Node.js) | Bun SQLite | Diferença |
|---|---|---|---|
| INSERT 1M linhas | 4.2s | 1.8s | 2.3x mais rápido |
| SELECT 100K linhas | 320ms | 140ms | 2.3x mais rápido |
| Commit transação | 12ms | 5ms | 2.4x mais rápido |
Os ganhos de performance vêm da integração do Bun com o JavaScriptCore e por evitar o overhead do N-API que os addons do Node.js têm.
2. Cliente S3 nativo
Armazenamento em nuvem sem dependências. O cliente S3 do Bun funciona com AWS S3, R2, MinIO e qualquer serviço compatível:
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, }); // Upload de arquivo const file = Bun.file("./large-video.mp4"); await s3.write("my-bucket/videos/intro.mp4", file); // Download de arquivo const downloaded = await s3.file("my-bucket/videos/intro.mp4"); await Bun.write("./downloaded.mp4", downloaded); // Streaming de arquivos grandes const stream = s3.file("my-bucket/data/huge.csv").stream(); for await (const chunk of stream) { // Processar chunk sem carregar tudo na memória }
O cliente S3 faz uploads multipart automaticamente pra arquivos grandes e suporta URLs pré-assinadas de cara. Compara com o AWS SDK:
// AWS SDK v3 - O jeito antigo 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 - O jeito novo const s3 = new S3Client({ /* config */ }); await s3.write("my-bucket/videos/intro.mp4", Bun.file("./large-video.mp4"));
A superfície de API é dramaticamente menor enquanto entrega a mesma funcionalidade.
3. Suporte a Postgres integrado
Postgres se junta ao SQLite como opção de banco integrada:
import { sql } from "bun"; // Conexão da variável de ambiente (DATABASE_URL) const users = await sql`SELECT * FROM users WHERE active = ${true}`; // Ou conexão explícita import { SQL } from "bun"; const db = new SQL({ hostname: "localhost", port: 5432, database: "myapp", username: "postgres", password: "secret", }); // Queries parametrizadas são automáticas const email = "[email protected]"; const user = await db`SELECT * FROM users WHERE email = ${email}`; // Transações 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`; });
A abordagem de template literal previne SQL injection por design e proporciona excelente DX.
4. Compatibilidade Node.js: 96% e subindo
O maior bloqueador pra adoção do Bun tem sido a compatibilidade. O Bun 1.2 agora passa:
- 96% da suite de testes do Node.js
- 100% dos testes do
node:fs - 100% dos testes do
node:path - 99% dos testes do
node:crypto - 98% dos testes do
node:http
Isso significa que a maioria dos pacotes npm "simplesmente funciona". Testamos vários pacotes populares:
| Pacote | Status | Notas |
|---|---|---|
| Express | ✅ Funciona | Compatibilidade total |
| Fastify | ✅ Funciona | Compatibilidade total |
| Prisma | ✅ Funciona | Desde o Bun 1.1 |
| Next.js | ⚠️ Parcial | Dev server funciona, alguns edge cases |
| NestJS | ✅ Funciona | Compatibilidade total |
| Socket.io | ✅ Funciona | Compatibilidade total |
5. Suporte Windows (Finalmente!)
Bun agora roda nativamente no Windows sem WSL:
powershell -c "irm bun.sh/install.ps1 | iex"
A performance no Windows é comparável ao Linux/macOS, o que não era o caso em versões anteriores.
Benchmark real: Construindo um servidor de API
Vamos construir a mesma API com Node.js e Bun pra ver diferenças reais de performance.
O teste: API CRUD de usuários com SQLite
Implementação 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 ) `); // Dados seed 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}`);
Implementação Node.js:
// server.mjs (Node.js com 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 do benchmark (M2 MacBook Pro, 10,000 requisições)
GET /users (ler 100 linhas):
| Runtime | Requisições/seg | Latência Média | Latência P99 |
|---|---|---|---|
| Node.js 22 | 12,450 | 7.8ms | 15ms |
| Bun 1.2 | 28,900 | 3.2ms | 8ms |
| Diferença | 2.3x mais rápido | 2.4x mais rápido | 1.9x mais rápido |
POST /users (inserir + retornar):
| Runtime | Requisições/seg | Latência Média | Latência P99 |
|---|---|---|---|
| Node.js 22 | 8,200 | 11.5ms | 22ms |
| Bun 1.2 | 19,400 | 4.8ms | 12ms |
| Diferença | 2.4x mais rápido | 2.4x mais rápido | 1.8x mais rápido |
Os resultados são consistentes: Bun é aproximadamente 2-2.5x mais rápido pra operações de API com banco de dados.
Comparação de tempo de inicialização
Cold start importa pra serverless:
| Runtime | Tempo de início | Memória inicial |
|---|---|---|
| Node.js 22 | 45ms | 52MB |
| Bun 1.2 | 8ms | 28MB |
| Diferença | 5.6x mais rápido | 46% menos |
Pra funções serverless, essa diferença é significativa.
Quando você deveria usar Bun em produção?
Baseado nos nossos testes, aqui vai uma avaliação realista:
✅ Bons candidatos pro Bun
-
Novos projetos com dependências simples
- APIs com SQLite/Postgres
- Workers em background
- Ferramentas CLI
- Microserviços
-
Aplicações críticas em performance
- APIs de alto throughput
- Aplicações em tempo real
- Funções edge
-
Funções serverless
- Quando cold start importa
- Quando custos de memória são uma preocupação
⚠️ Prossiga com cuidado
-
Aplicações grandes de Next.js
- Funciona na maioria dos casos, mas existem edge cases
- Teste bem antes de fazer deploy
-
Aplicações com addons nativos do Node.js
- Alguns addons nativos podem não funcionar
- Verifique compatibilidade primeiro
-
Codebases legados com dependências profundas do Node.js
- O esforço de migração pode não valer a pena
❌ Não recomendado (ainda)
-
Aplicações Electron
- Sem suporte ao Electron
-
Aplicações que precisam de
node:vm- Suporte a VM limitado
-
Sistemas financeiros de missão crítica
- Espere mais testes em produção
Guia de migração: Saindo do Node.js pro Bun
Se você decidiu testar o Bun, aqui está como migrar:
Passo 1: Instalar o Bun
curl -fsSL https://bun.sh/install | bash
Passo 2: Verificar compatibilidade
# Rodar testes existentes com Bun bun test # Verificar problemas bun run your-script.ts
Passo 3: Atualizar scripts do package.json
{ "scripts": { "dev": "bun run --watch src/index.ts", "start": "bun run src/index.ts", "test": "bun test" } }
Passo 4: Remover dependências desnecessárias
Com os recursos integrados do Bun, você geralmente pode remover:
better-sqlite3→ Usebun:sqlite@aws-sdk/client-s3→ UseBun.S3Clientpg→ UsebunSQLdotenv→ Bun carrega.envautomaticamentets-node/tsx→ Não são necessários
Passo 5: Atualizar 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"]
O elefante na sala: Dá pra confiar?
Bun é desenvolvido pela Oven, uma startup. A pergunta natural: e se a empresa quebrar?
Argumentos pra confiar:
- Open source (licença MIT)
- Comunidade grande (70k+ estrelas no GitHub)
- Ex-membros do time WebKit/Safari envolvidos
- Adoção empresarial crescendo
Argumentos pra ter cuidado:
- Node.js tem 15+ anos de batalha
- Alta dependência de pessoas-chave (se Jarred Sumner sair, impacto grande)
- Alguns edge cases ainda existem
Nossa recomendação: Comece por projetos novos e não críticos. Se der certo, expanda gradualmente.
Conclusão: O ponto de virada?
Bun 1.2 representa um marco significativo:
- Performance: 2-3x mais rápido é real, não marketing
- DX: Menos dependências, APIs mais simples
- Compatibilidade: 96% da suite de testes do Node.js
- Recursos: Ferramentas integradas que precisariam de 5+ pacotes npm
O Bun está pronto pra substituir o Node.js? Pra novos projetos: cada vez mais sim. Pra sistemas existentes em produção: migre com cuidado e incrementalmente.
Uma coisa é clara: ignorar o Bun não é mais uma opção. Teste no seu próximo projeto. Você pode se surpreender.
# Instalar e rodar seu primeiro projeto Bun curl -fsSL https://bun.sh/install | bash bun init bun run index.ts
Bem-vindo ao futuro dos runtimes JavaScript.