Back

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çãobetter-sqlite3 (Node.js)Bun SQLiteDiferença
INSERT 1M linhas4.2s1.8s2.3x mais rápido
SELECT 100K linhas320ms140ms2.3x mais rápido
Commit transação12ms5ms2.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:

PacoteStatusNotas
Express✅ FuncionaCompatibilidade total
Fastify✅ FuncionaCompatibilidade total
Prisma✅ FuncionaDesde o Bun 1.1
Next.js⚠️ ParcialDev server funciona, alguns edge cases
NestJS✅ FuncionaCompatibilidade total
Socket.io✅ FuncionaCompatibilidade 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):

RuntimeRequisições/segLatência MédiaLatência P99
Node.js 2212,4507.8ms15ms
Bun 1.228,9003.2ms8ms
Diferença2.3x mais rápido2.4x mais rápido1.9x mais rápido

POST /users (inserir + retornar):

RuntimeRequisições/segLatência MédiaLatência P99
Node.js 228,20011.5ms22ms
Bun 1.219,4004.8ms12ms
Diferença2.4x mais rápido2.4x mais rápido1.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:

RuntimeTempo de inícioMemória inicial
Node.js 2245ms52MB
Bun 1.28ms28MB
Diferença5.6x mais rápido46% 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

  1. Novos projetos com dependências simples

    • APIs com SQLite/Postgres
    • Workers em background
    • Ferramentas CLI
    • Microserviços
  2. Aplicações críticas em performance

    • APIs de alto throughput
    • Aplicações em tempo real
    • Funções edge
  3. Funções serverless

    • Quando cold start importa
    • Quando custos de memória são uma preocupação

⚠️ Prossiga com cuidado

  1. Aplicações grandes de Next.js

    • Funciona na maioria dos casos, mas existem edge cases
    • Teste bem antes de fazer deploy
  2. Aplicações com addons nativos do Node.js

    • Alguns addons nativos podem não funcionar
    • Verifique compatibilidade primeiro
  3. 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)

  1. Aplicações Electron

    • Sem suporte ao Electron
  2. Aplicações que precisam de node:vm

    • Suporte a VM limitado
  3. 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 → Use bun:sqlite
  • @aws-sdk/client-s3 → Use Bun.S3Client
  • pg → Use bun SQL
  • dotenv → Bun carrega .env automaticamente
  • ts-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:

  1. Performance: 2-3x mais rápido é real, não marketing
  2. DX: Menos dependências, APIs mais simples
  3. Compatibilidade: 96% da suite de testes do Node.js
  4. 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.

bunnodejsjavascriptruntimesqlitedatabasebackendperformance