Apidog

Plataforma Colaborativa All-in-one para Desenvolvimento de API

Design de API

Documentação de API

Depuração de API

Mock de API

Testes Automatizados de API

Começando com a API Bun

Mark Ponomarev

Mark Ponomarev

Updated on maio 4, 2025

Entre as ferramentas de desenvolvimento recentes mais empolgantes está o Bun, um kit de ferramentas JavaScript incrivelmente rápido e tudo-em-um, projetado para aumentar a produtividade do desenvolvedor e o desempenho da aplicação. O Bun não é apenas mais um runtime; é um ecossistema coeso que engloba um runtime, bundler, executor de testes, gerenciador de pacotes e muito mais, tudo dentro de um único executável nativo. Este guia irá guiá-lo pelos fundamentos do Bun, focando em seus conceitos centrais e APIs poderosas.

💡
Quer uma ótima ferramenta de teste de API que gere documentação de API bonita?

Quer uma plataforma integrada, tudo-em-um, para sua equipe de desenvolvimento trabalhar em conjunto com produtividade máxima?

O Apidog entrega todas as suas demandas e substitui o Postman por um preço muito mais acessível!
button

O Que é o Bun?

Diagrama comparando Bun, Node.js e Deno em termos de runtime, bundler, transpiler, package manager e test runner.

Em sua essência, o Bun é construído em torno de um motor de tempo de execução JavaScript de alto desempenho. Diferente do Node.js, que usa o motor V8 do Google, o Bun utiliza o JavaScriptCore (JSC) da Apple, o motor que impulsiona o Safari. Essa escolha, combinada com a implementação do Bun na linguagem de programação de baixo nível Zig, contribui significativamente para sua velocidade notável. Os tempos de inicialização para aplicações Bun são frequentemente medidos como drasticamente mais rápidos do que aplicações Node.js equivalentes, às vezes por um fator de quatro ou mais. Essa vantagem de velocidade se estende além da inicialização; as implementações internas do Bun de várias APIs são otimizadas para máxima vazão e uso mínimo de memória.

Mas a ambição do Bun vai muito além de ser apenas um runtime mais rápido. Ele visa ser um kit de ferramentas abrangente, abordando diretamente as necessidades comuns dos desenvolvedores:

  1. Ferramentas Integradas: O próprio comando bun atua como o ponto de entrada para inúmeras funcionalidades. bun run executa scripts definidos em package.json, bun install gerencia dependências (muitas vezes muito mais rápido que npm ou yarn), bun test executa testes usando uma API compatível com Jest, bun build empacota código para produção, e bunx executa pacotes sem instalá-los explicitamente. Essa integração simplifica fluxos de trabalho, reduzindo o número de ferramentas de desenvolvimento distintas necessárias.
  2. Suporte Nativo a TypeScript e JSX: Uma das características de destaque do Bun é seu suporte pronto para uso a arquivos TypeScript (.ts, .tsx) e JSX (.jsx). O Bun inclui um transpilador integrado, altamente otimizado, que lida com esses tipos de arquivo de forma transparente durante a execução ou empacotamento. Isso elimina a necessidade de etapas de compilação separadas envolvendo tsc ou Babel para muitos cenários de desenvolvimento comuns, otimizando o processo de configuração.
  3. Compatibilidade do Sistema de Módulos: O Bun abraça o JavaScript moderno com suporte de primeira classe para Módulos ES (ESM). No entanto, ele reconhece o vasto ecossistema existente construído em CommonJS (CJS). O Bun oferece compatibilidade robusta para ambos os sistemas de módulos, permitindo que os desenvolvedores usem import e require de forma amplamente intercambiável e aproveitem os milhões de pacotes CJS existentes disponíveis no npm.
  4. Adesão às APIs Padrão Web: Um princípio de design chave é a implementação de APIs Padrão Web. Funções e objetos como fetch, Request, Response, Headers, WebSocket e a API Streams (ReadableStream, WritableStream) são integrados ao escopo global do Bun. Isso promove a portabilidade de código entre ambientes Bun no lado do servidor, frontends de navegador e plataformas de edge computing, permitindo que os desenvolvedores reutilizem APIs familiares em diferentes contextos.
  5. Compatibilidade com Node.js: Enquanto trilha seu próprio caminho com APIs otimizadas e padrões Web, o Bun busca um alto grau de compatibilidade com a superfície da API do Node.js. Muitos módulos Node.js integrados (node:fs, node:path, node:os, node:events, etc.) e objetos globais (process, Buffer, __filename, __dirname) são parcial ou totalmente implementados. O objetivo é permitir que muitos projetos Node.js existentes e pacotes npm rodem no Bun com modificação mínima ou nenhuma, posicionando o Bun como um potencial "substituto direto" em numerosos casos.

Ao combinar esses elementos, o Bun apresenta uma alternativa atraente para desenvolvedores JavaScript e TypeScript que buscam desempenho, simplicidade e uma experiência de desenvolvimento moderna.

Como Instalar o Bun

Começar com o Bun é projetado para ser rápido e direto em várias plataformas. O método mais comum para macOS, Linux e Windows Subsystem for Linux (WSL) usa um simples comando curl executado no seu terminal:

curl -fsSL https://bun.sh/install | bash

Este comando baixa um script de instalação e o direciona diretamente para o bash. O script lida com a detecção do seu sistema operacional e arquitetura, baixa o executável apropriado do Bun e geralmente o instala em ~/.bun/bin. Ele também tenta atualizar o arquivo de configuração do seu shell (como .zshrc, .bashrc ou .bash_profile) para adicionar ~/.bun/bin ao PATH do seu sistema, tornando o comando bun disponível globalmente. Pode ser necessário reiniciar sua sessão de terminal ou obter manualmente seu arquivo de configuração de shell (por exemplo, source ~/.zshrc) para que as mudanças entrem em vigor imediatamente.

Se você encontrar problemas de permissão ou preferir não direcionar diretamente para o bash, você pode baixar o script primeiro e inspecioná-lo antes de executar:

curl -fsSL https://bun.sh/install -o install.sh
# Inspect install.sh if desired
bash install.sh

Outros Métodos de Instalação:

  • NPM: Embora destinado principalmente como uma ferramenta independente, você também pode instalar o Bun globalmente via npm, o que pode ser conveniente em ambientes onde Node.js e npm já estão presentes:
npm install -g bun
  • Docker: Imagens oficiais do Bun Docker estão disponíveis no Docker Hub, fornecendo ambientes isolados com o Bun pré-instalado. Estes são úteis para fluxos de trabalho de desenvolvimento e implantação em contêineres. Você pode encontrar várias imagens baseadas em diferentes distribuições de SO base (como Debian, Alpine) e tags correspondentes a versões específicas do Bun.
docker run --rm --init --ulimit memlock=-1:-1 oven/bun:latest bun --version
  • Windows: O suporte nativo para Windows para o Bun ainda é considerado experimental, mas está sendo ativamente desenvolvido. A maneira recomendada de usar o Bun no Windows atualmente é via WSL. No entanto, compilações diretas para Windows estão se tornando disponíveis, e o processo de instalação pode envolver o download de um arquivo .zip e a adição manual do local do executável ao PATH do sistema. Verifique a documentação oficial do Bun para o status mais recente e instruções para instalação nativa no Windows.
  • Homebrew (macOS): Se você usa Homebrew no macOS, você pode instalar o Bun via seu tap:
brew tap oven-sh/bun
brew install bun

Verificação:

Uma vez instalado, abra uma nova janela de terminal e verifique a instalação verificando a versão:

bun --version

Isso deve exibir o número da versão instalada, confirmando que o Bun está pronto para usar. Você também pode executar bun --help para ver uma lista de comandos e opções disponíveis.

Execute Seu Bun Pela Primeira Vez

Vamos mergulhar na escrita e execução de um programa simples com o Bun. Uma das tarefas mais comuns é criar um servidor HTTP. O Bun fornece uma API integrada, altamente otimizada, para este propósito: Bun.serve.

Crie um novo arquivo chamado server.js (ou server.ts, já que o Bun lida com ambos):

// server.ts

// Bun.serve inicia o servidor HTTP
const server = Bun.serve({
  // Especifica a porta para ouvir.
  // O padrão é process.env.PORT || 3000
  port: 3000,

  // A função 'fetch' é o manipulador de requisição principal.
  // Ela recebe um objeto Request padrão e deve retornar um objeto Response (ou uma Promise que resolva para um).
  fetch(request: Request): Response {
    // Cria um objeto Response padrão da API Web
    return new Response("Welcome to Bun!");
  },
});

// Registra uma mensagem indicando que o servidor está rodando
console.log(`Listening on http://localhost:${server.port}`);

Este trecho de código faz o seguinte:

  1. Ele chama Bun.serve, a função principal para criar servidores HTTP no Bun.
  2. Ele passa um objeto de configuração, especificando a port (3000 neste caso).
  3. A parte crucial é a função fetch. Esta função é invocada para cada requisição HTTP de entrada. Ela se alinha com o padrão de manipulador de eventos fetch do Service Worker, aceitando um objeto Request padrão.
  4. Dentro de fetch, construímos e retornamos um objeto Response padrão. Aqui, simplesmente retornamos o texto simples "Welcome to Bun!".
  5. Finalmente, registramos uma mensagem de confirmação no console, incluindo a porta real em que o servidor está ouvindo (acessível via server.port).

Para executar este servidor, abra seu terminal no diretório onde você salvou o arquivo e execute:

bun run server.ts

Ou, se você salvou como server.js:

bun run server.js

O Bun executará o script. Se você usou TypeScript (server.ts), o transpilador interno do Bun lidará com a conversão para JavaScript em tempo real antes da execução. Você verá a mensagem "Listening on http://localhost:3000".

Agora, abra seu navegador web e navegue para http://localhost:3000. Você deverá ver o texto "Welcome to Bun!" exibido.

Para parar o servidor, volte para o seu terminal e pressione Ctrl + C.

Este exemplo simples demonstra a facilidade de configurar um servidor básico e executar código (incluindo TypeScript) diretamente com o Bun, mostrando sua natureza integrada e dependência de APIs Padrão Web como Request e Response.

Qual o Suporte Nativo a TypeScript no Bun

Uma das vantagens mais significativas do Bun, especialmente para desenvolvedores que já usam ou desejam adotar TypeScript, é seu suporte de primeira classe, pronto para uso. Diferente do Node.js, onde executar TypeScript geralmente requer pré-compilação usando o compilador TypeScript (tsc) ou usando carregadores/registradores como ts-node ou tsx, o Bun lida com isso nativamente e de forma transparente.

Como Funciona:

Quando você pede ao Bun para executar um arquivo .ts ou .tsx (por exemplo, bun run myscript.ts), ele invoca automaticamente seu transpilador interno. Este transpilador é escrito em código nativo (Zig) e é extremamente rápido. Seu trabalho é:

  1. Remover Tipos: Remover anotações de tipo TypeScript, interfaces, enums, etc., pois não fazem parte da execução padrão do JavaScript.
  2. Transformar Sintaxe: Converter sintaxe específica do TypeScript (como certos usos de enum ou sintaxe de decorador mais antiga, se configurado) em JavaScript equivalente.
  3. Lidar com JSX: Transformar sintaxe JSX (usada em arquivos .tsx e .jsx) em chamadas de função JavaScript padrão (tipicamente React.createElement ou um equivalente de tempo de execução JSX configurado).

O benefício chave é que isso acontece em tempo real durante o processo de execução (bun run) ou empacotamento (bun build). Não há uma etapa de build separada e explícita necessária apenas para executar seu código TypeScript durante o desenvolvimento.

Exemplo:

Considere este arquivo TypeScript (greet.ts):

// greet.ts
interface User {
  name: string;
  id: number;
}

function greetUser(user: User): void {
  console.log(`Hello, ${user.name} (ID: ${user.id})!`);
}

const myUser: User = { name: "Bun Developer", id: 123 };

greetUser(myUser);

// Você pode até usar recursos modernos que o Bun suporta
const message = `Bun version: ${Bun.version}`;
console.log(message);

Você pode executar isso diretamente:

bun run greet.ts

O Bun irá transpilar internamente e executar o JavaScript resultante, produzindo uma saída como:

Hello, Bun Developer (ID: 123)!
Bun version: 1.x.y

Suporte a JSX:

Da mesma forma, se você tiver um arquivo .tsx com JSX:

// component.tsx

// Assumindo que você tenha JSX configurado (as configurações padrão do Bun geralmente funcionam com React)
function MyComponent({ name }: { name: string }) {
  return <div className="greeting">Hello, {name}!</div>;
}

// NOTA: Executar isso diretamente não renderizará HTML,
// apenas mostra a estrutura JS transpilada.
// Você normalmente usaria isso dentro de um aplicativo ou framework maior.
console.log("Simulating component creation (transpiled output structure):");
// A saída real depende das configurações de transformação JSX,
// mas seriam objetos/chamadas de função JavaScript.

Executar bun run component.tsx executaria o arquivo, transpilando o JSX em JavaScript.

Configuração (tsconfig.json):

O Bun respeita os arquivos tsconfig.json para opções de configuração que afetam a transpilação. Embora não execute verificação de tipo completa como o tsc (o Bun foca na velocidade de execução e transpilação), ele lê tsconfig.json para entender configurações como:

  • jsx: ("react-jsx", "react", etc.) Como o JSX deve ser transformado.
  • jsxImportSource: O módulo para importar funções auxiliares de JSX de (por exemplo, "react").
  • experimentalDecorators, emitDecoratorMetadata: Suporte para decoradores.
  • paths, baseUrl: Mapeamento de caminho de módulo para aliases de importação personalizados.
  • target, module: Embora o Bun gerencie a execução, estas podem às vezes influenciar detalhes menores da transpilação.
  • strict, strictNullChecks, etc.: Estes afetam principalmente a verificação de tipo (o que o Bun não faz durante a execução), mas algum comportamento relacionado à emissão de JavaScript pode ser influenciado.

Se nenhum tsconfig.json for encontrado, o Bun usa configurações padrão sensatas.

Esta integração perfeita torna o início de projetos TypeScript com o Bun incrivelmente simples e rápido, diminuindo a barreira de entrada e acelerando ciclos de desenvolvimento.

Vamos Falar Sobre APIs Específicas do Bun

Embora o Bun enfatize fortemente a compatibilidade com APIs Padrão Web e Node.js, ele também introduz seu próprio conjunto de APIs otimizadas e integradas sob o objeto global Bun. Essas APIs frequentemente fornecem alternativas de alto desempenho ou wrappers convenientes para tarefas comuns que aproveitam as capacidades de código nativo do Bun.

Aqui está um vislumbre de algumas APIs Bun.* chave:

  • Bun.serve({...}): Como visto no Guia Rápido, esta é a pedra angular para construir servidores HTTP e WebSocket de alto desempenho. Oferece uma configuração simplificada e usa a assinatura padrão do manipulador fetch. (Coberto em detalhes posteriormente).
  • Bun.file(path): Cria um objeto BunFile, que é uma referência preguiçosa a um arquivo no disco. Fornece métodos altamente otimizados para ler conteúdo de arquivos em vários formatos (.text(), .json(), .arrayBuffer(), .stream()) apenas quando necessário. Isso é frequentemente muito mais rápido que equivalentes em node:fs.
  • Bun.write(path, data): A contraparte de Bun.file, usada para escrever dados em arquivos de forma eficiente. Aceita vários tipos de dados (strings, Blobs, Buffers, outros BunFiles) e realiza escritas atômicas por padrão.
  • Bun.build({...}): Fornece acesso programático ao empacotador integrado do Bun, que é compatível com a API esbuild. Permite empacotar JavaScript/TypeScript para navegadores ou outros runtimes diretamente de dentro de scripts Bun.
  • Bun.spawn({...}) / Bun.spawnSync({...}): Executar comandos externos como processos filhos, similar ao child_process do Node.js. Oferece APIs de streaming assíncronas e uma versão síncrona mais simples, otimizada para baixa sobrecarga.
  • Bun.Transpiler({...}): Acesso programático direto ao transpilador interno rápido do Bun para converter TypeScript/JSX em JavaScript sem empacotamento completo.
  • Bun.password.hash(...) / Bun.password.verify(...): Funções seguras e fáceis de usar para hashing e verificação de senhas usando algoritmos padrão da indústria como Argon2id (recomendado) e bcrypt. Evita a necessidade de bibliotecas externas.
  • Bun.env: Um objeto que fornece acesso a variáveis de ambiente, similar a process.env, mas potencialmente oferecendo acesso mais rápido em alguns cenários.
  • Bun.version: Uma string contendo a versão do Bun atualmente em execução.
  • Bun.revision: Uma string contendo o hash do commit Git da build do Bun atualmente em execução.
  • Bun.sleep(ms) / Bun.sleepSync(ms): Funções para pausar a execução por uma duração especificada.
  • Bun.gc(): Acionar manualmente a coleta de lixo (usar com moderação, principalmente para depuração/benchmarking).
  • Bun.resolveSync(specifier, parentPath) / Bun.resolve(specifier, parentPath): Realizar programaticamente a resolução de módulo no estilo Node.js para encontrar o caminho absoluto de um módulo.

Essas APIs representam o esforço do Bun em fornecer soluções otimizadas e integradas para tarefas de desenvolvimento comuns, reduzindo a dependência de dependências externas e aproveitando a velocidade de seu núcleo nativo.

APIs Web no Bun

Uma escolha de design fundamental no Bun é seu forte compromisso em implementar APIs Padrão Web. Onde quer que exista uma API padrão para uma tarefa específica (especialmente para rede e tratamento de dados), o Bun prefere implementar esse padrão em vez de inventar uma API proprietária ou confiar apenas em convenções do Node.js.

Esta abordagem oferece várias vantagens significativas:

  1. Portabilidade de Código: Código escrito usando APIs Padrão Web frequentemente pode ser reutilizado em diferentes ambientes JavaScript – o navegador, Node.js (que também está cada vez mais adotando padrões Web), Deno, Cloudflare Workers e Bun – com menos modificações.
  2. Familiaridade: Desenvolvedores já familiarizados com APIs de navegador podem aproveitar esse conhecimento ao trabalhar com o Bun, reduzindo a curva de aprendizado.
  3. Preparação para o Futuro: Alinhar-se com padrões estabelecidos por órgãos como WHATWG e W3C geralmente leva a APIs mais estáveis e amplamente suportadas a longo prazo.
  4. Desempenho: As implementações nativas do Bun dessas APIs Web são altamente otimizadas para seu tempo de execução.

As principais APIs Padrão Web implementadas no Bun incluem:

API Fetch:

  • fetch(): A função global para fazer requisições HTTP(S).
  • Request: Representa uma requisição HTTP.
  • Response: Representa uma resposta HTTP.
  • Headers: Representa cabeçalhos HTTP.

API de URL:

  • URL: Para analisar e manipular URLs.
  • URLSearchParams: Para trabalhar com parâmetros de consulta de URL.

API de Streams:

  • ReadableStream: Representa uma fonte de dados que pode ser lida assincronamente. Usado para corpos de requisição/resposta, leitura de arquivos, etc.
  • WritableStream: Representa um destino para dados que pode ser escrita assincronamente. Usado para corpos de requisição, escrita de arquivos, etc.
  • TransformStream: Um stream duplex que transforma dados conforme passa por ele (por exemplo, compressão, codificação).

API de Codificação:

  • TextEncoder: Codifica strings em Uint8Array (tipicamente UTF-8).
  • TextDecoder: Decodifica Uint8Array em strings.

API de Blob:

  • Blob: Representa dados brutos imutáveis, frequentemente usado para objetos tipo arquivo.
  • File: Estende Blob para representar arquivos, incluindo metadados como nome e data de última modificação. (Frequentemente criado via Bun.file().slice() ou de dados de formulário).

API de FormData:

  • FormData: Para construir conjuntos de pares chave/valor, frequentemente usado para enviar dados de formulário em requisições fetch.

API de WebSocket:

  • WebSocket: A API do lado do cliente para estabelecer conexões WebSocket. (O tratamento do lado do servidor é integrado em Bun.serve).

Timers:

  • setTimeout, setInterval, clearTimeout, clearInterval: Funções padrão para agendar a execução de código.

API de Console:

  • console.log, console.error, console.warn, etc.: Funções de log padrão.

API de Criptografia:

  • crypto.subtle: Fornece acesso a primitivas criptográficas de baixo nível (hashing, assinatura, criptografia).
  • crypto.randomUUID(): Gera UUIDs v4.
  • crypto.getRandomValues(): Gera números aleatórios criptograficamente fortes.

API de Performance:

  • performance.now(): Fornece timestamps de alta resolução para medições de performance.

Ao fornecer implementações robustas e performáticas dessas APIs Web essenciais, o Bun posiciona-se como um runtime moderno bem adequado para construir servidores web, APIs e outras aplicações centradas em rede, usando interfaces familiares e padronizadas.

Servidor HTTP do Bun, Explicado

A maneira principal de criar servidores web no Bun é através da API Bun.serve. Ela é projetada para desempenho excepcional e facilidade de uso, integrando-se perfeitamente com APIs Padrão Web como Request, Response e fetch.

Conceitos Principais:

A função Bun.serve recebe um objeto de configuração e retorna um objeto Server. A parte mais crítica da configuração é a função fetch.

import { type Server } from "bun";

const server: Server = Bun.serve({
  port: 8080, // A porta para ouvir
  hostname: "0.0.0.0", // A interface de rede para vincular (0.0.0.0 para todas)

  // fetch: O coração do servidor - lida com requisições de entrada
  async fetch(req: Request, server: Server): Promise<Response> {
    // req é um objeto Request padrão da API Web
    // server é uma referência à própria instância do Server

    const url = new URL(req.url);

    // Exemplo Básico de Roteamento
    if (url.pathname === "/") {
      return new Response("Homepage");
    }
    if (url.pathname === "/about") {
      return new Response("About Us page");
    }
    if (url.pathname === "/greet" && req.method === "GET") {
        const name = url.searchParams.get("name") || "World";
        return new Response(`Hello, ${name}!`);
    }
     if (url.pathname === "/data" && req.method === "POST") {
        try {
            const data = await req.json(); // Ler corpo da requisição como JSON
            console.log("Received data:", data);
            return new Response(JSON.stringify({ received: data }), {
               headers: { 'Content-Type': 'application/json' }
            });
        } catch (e) {
            return new Response("Invalid JSON body", { status: 400 });
        }
    }

    // Padrão 404 Não Encontrado
    return new Response("Page Not Found", { status: 404 });
  },

  // error: Manipulador opcional para erros que ocorrem *fora* do manipulador fetch
  error(error: Error): Response | Promise<Response> {
    console.error("[Server Error]", error);
    return new Response("Something went wrong!", { status: 500 });
  },

  // development: Defina como true para páginas de erro de desenvolvimento úteis (padrão: !process.env.NODE_ENV=production)
   development: true,

   // Outras opções como 'websocket', 'tls' existem para casos de uso avançados
});

console.log(`Bun server listening on http://${server.hostname}:${server.port}`);

// Você pode interagir com o objeto server:
// server.stop() // Para o servidor
// server.reload({...}) // Atualiza a configuração do servidor dinamicamente (por exemplo, manipulador fetch)

Principais Recursos:

  • Desempenho: Bun.serve é construído sobre a implementação de servidor HTTP personalizada e altamente otimizada do Bun, escrita em Zig. É capaz de lidar com um número muito alto de requisições por segundo com baixa latência e consumo de recursos, comparado a muitos frameworks Node.js.
  • Manipulador fetch: Usar a assinatura padrão (Request) => Response | Promise<Response> torna a lógica central familiar para qualquer pessoa que tenha trabalhado com Service Workers, Cloudflare Workers ou outros frameworks web modernos. Incentiva o uso de objetos Request e Response padrão.
  • Objeto Request: O parâmetro req fornece acesso a propriedades e métodos Request padrão: req.url, req.method, req.headers, req.json(), req.text(), req.arrayBuffer(), req.formData(), req.body (um ReadableStream).
  • Objeto Response: Você cria e retorna objetos Response padrão, permitindo que você defina o corpo (string, Buffer, Blob, Stream, etc.), código de status e cabeçalhos facilmente.
  • Tratamento de Erros: A função opcional error fornece um local centralizado para capturar erros que ocorrem antes ou durante o processamento inicial de uma requisição pelo próprio servidor (por exemplo, handshake TLS falhou, requisição malformada), ou erros lançados sincronicamente fora do try...catch do manipulador fetch. Erros dentro do manipulador fetch assíncrono devem tipicamente ser capturados lá.
  • Streaming: Tanto os corpos de requisição quanto de resposta podem ser transmitidos via stream usando as APIs padrão ReadableStream e WritableStream, essencial para lidar com uploads ou downloads grandes de forma eficiente.
  • Recarregamento Dinâmico: O método server.reload() permite atualizar opções do servidor, incluindo os manipuladores fetch e error, sem precisar de uma reinicialização completa do servidor, o que é útil para configurações de hot module replacement (HMR).

Bun.serve fornece uma base poderosa, mas simples, para construir aplicações web e APIs no Bun, priorizando velocidade e adesão a padrões web.

Cliente Fetch do Bun

Complementando a API do servidor, o Bun fornece uma função global fetch para fazer requisições HTTP(S) de saída. Esta implementação adere estritamente ao padrão WHATWG Fetch, tornando-o familiar para desenvolvedores web e garantindo consistência com a função fetch usada dentro do Bun.serve. A implementação nativa do Bun garante alto desempenho para tarefas de rede do lado do cliente.

Fazendo Requisições:

O uso básico envolve chamar fetch com uma URL e, opcionalmente, um objeto de configuração:

async function makeRequests() {
  const url = "https://httpbin.org"; // Um serviço útil para testar requisições HTTP

  // --- Requisição GET Básica ---
  console.log("--- GET Request ---");
  try {
    const getResponse = await fetch(`${url}/get?param1=value1`);

    console.log(`Status: ${getResponse.status} ${getResponse.statusText}`);

    // Verificar se a requisição foi bem-sucedida (status 200-299)
    if (!getResponse.ok) {
      throw new Error(`HTTP error! status: ${getResponse.status}`);
    }

    // Acessar cabeçalhos
    console.log("Content-Type Header:", getResponse.headers.get('content-type'));

    // Ler o corpo da resposta como JSON
    const getData = await getResponse.json();
    console.log("Response JSON:", getData.args); // httpbin.org/get retorna parâmetros de consulta em 'args'

  } catch (error) {
    console.error("GET request failed:", error);
  }

  // --- Requisição POST com corpo JSON ---
  console.log("\n--- POST Request (JSON) ---");
  try {
     const postData = { name: "Bun", type: "Runtime" };
     const postResponse = await fetch(`${url}/post`, {
        method: "POST",
        headers: {
          // Indicar que estamos enviando JSON
          "Content-Type": "application/json",
          "Accept": "application/json", // Indicar que preferimos JSON de volta
          "X-Custom-Header": "BunFetchExample",
        },
        // O corpo deve ser stringificado para JSON
        body: JSON.stringify(postData),
     });

     if (!postResponse.ok) {
        throw new Error(`HTTP error! status: ${postResponse.status}`);
     }

     const postResult = await postResponse.json();
     console.log("POST Response JSON:", postResult.json); // httpbin.org/post retorna o JSON postado em 'json'

  } catch (error) {
     console.error("POST request failed:", error);
  }

   // --- Requisição com Timeout ---
   console.log("\n--- GET Request with Timeout ---");
   try {
      const controller = new AbortController();
      const timeoutId = setTimeout(() => controller.abort(), 2000); // Abortar após 2 segundos

      const timeoutResponse = await fetch(`${url}/delay/5`, { // Este endpoint espera 5 segundos
         signal: controller.signal // Passar o AbortSignal
      });

      clearTimeout(timeoutId); // Limpar timeout se o fetch completar mais rápido
      console.log("Timeout request succeeded (unexpected for /delay/5)");

   } catch (error: any) {
      // AbortError é lançado quando o sinal aborta
      if (error.name === 'AbortError') {
         console.log("Fetch abortado devido a timeout, como esperado.");
      } else {
         console.error("Timeout request failed:", error);
      }
   }
}

await makeRequests();

Principais Recursos e Opções:

API Padrão: Usa a assinatura familiar fetch(url, options).

Métodos: Suporta todos os métodos HTTP padrão (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS). Especificado via a opção method.

Cabeçalhos: Cabeçalhos são definidos usando a opção headers, que pode ser um objeto Headers ou um objeto simples chave-valor.

Corpo da Requisição: A opção body aceita vários tipos:

  • string: Enviado como texto (comum para JSON, XML, texto simples).
  • ArrayBuffer / TypedArray: Enviado como dados binários.
  • Blob / File: Enviado como dados binários, frequentemente com Content-Type apropriado.
  • ReadableStream: Transmite o conteúdo do corpo via stream, adequado para uploads grandes.
  • URLSearchParams: Define automaticamente o Content-Type para application/x-www-form-urlencoded.
  • FormData: Define automaticamente o Content-Type para multipart/form-data.

Tratamento de Resposta: A chamada fetch retorna uma Promise que resolve para um objeto Response.

  • response.ok: Booleano indicando um status HTTP bem-sucedido (200-299).
  • response.status: O código de status HTTP (por exemplo, 200, 404).
  • response.statusText: A mensagem de status HTTP (por exemplo, "OK", "Not Found").
  • response.headers: Um objeto Headers para acessar cabeçalhos de resposta.
  • Métodos de Leitura do Corpo: response.json(), response.text(), response.arrayBuffer(), response.blob(), response.formData(). Estes retornam Promises.
  • response.body: Um ReadableStream para transmitir o corpo da resposta via stream.

Timeouts e Cancelamento: Use AbortController e AbortSignal para implementar timeouts ou cancelar requisições manualmente. Passe o signal para as opções do fetch.

Redirecionamentos: Controlado pela opção redirect ('follow', 'manual', 'error'). O padrão é 'follow'.

  • Caching: Controlado pela opção cache (por exemplo, 'no-store', 'reload').

O fetch do Bun fornece uma maneira performática e compatível com padrões para interagir com recursos HTTP, integrando-se perfeitamente ao ecossistema Bun.

WebSockets do Bun

WebSockets fornecem uma maneira de estabelecer canais de comunicação persistentes e bidirecionais entre um cliente e um servidor sobre uma única conexão TCP. O Bun oferece excelente suporte para WebSockets, tanto para criar clientes WebSocket quanto para lidar com conexões WebSocket no lado do servidor dentro do Bun.serve.

1. Cliente WebSocket:

O Bun implementa a API WebSocket padrão do navegador para criar conexões de cliente.

// websocket-client.ts

const wsUrl = "wss://ws.postman-echo.com/raw"; // Servidor de eco público

console.log(`Connecting client to ${wsUrl}...`);

const socket = new WebSocket(wsUrl);

// Evento: Conexão aberta com sucesso
socket.addEventListener("open", (event) => {
  console.log("[Client] WebSocket connection established!");
  socket.send("Hello from Bun client!");

  // Enviar dados binários após um pequeno atraso
  setTimeout(() => {
     const binaryData = new TextEncoder().encode("Bun binary data");
     console.log("[Client] Sending binary:", binaryData);
     socket.send(binaryData);
  }, 500);

   // Fechar a conexão após algum tempo
  setTimeout(() => {
     console.log("[Client] Closing connection...");
     socket.close(1000, "Client done testing"); // 1000 = Fechamento normal
  }, 2000);
});

// Evento: Mensagem recebida do servidor
socket.addEventListener("message", async (event) => {
  // event.data pode ser string, Blob ou ArrayBuffer
  let messageContent: string | Uint8Array;
  if (typeof event.data === "string") {
    messageContent = event.data;
    console.log(`[Client] Received string: "${messageContent}"`);
  } else if (event.data instanceof Blob) {
    // O Bun frequentemente recebe dados binários como Blobs da API WebSocket
    const arrayBuffer = await event.data.arrayBuffer();
    messageContent = new Uint8Array(arrayBuffer);
    console.log(`[Client] Received binary (Blob):`, messageContent, `(${new TextDecoder().decode(messageContent)})`);
  } else if (event.data instanceof ArrayBuffer) {
     messageContent = new Uint8Array(event.data);
     console.log(`[Client] Received binary (ArrayBuffer):`, messageContent, `(${new TextDecoder().decode(messageContent)})`);
  } else {
    console.log("[Client] Received unknown message type:", event.data);
  }
});

// Evento: Ocorreu um erro (problema de rede, etc.)
socket.addEventListener("error", (event) => {
  // O objeto event frequentemente carece de detalhes específicos. Verifique logs do console/rede.
  console.error("[Client] WebSocket error:", event.type);
});

// Evento: Conexão fechada
socket.addEventListener("close", (event) => {
  console.log(
    `[Client] WebSocket closed. Code: ${event.code}, Reason: "${event.reason}", Clean: ${event.wasClean}`
  );
  // Sair do script de forma limpa assim que o socket for fechado
  process.exit(0);
});

// Manter ativo brevemente para eventos, saída tratada no listener 'close'
// setInterval(() => {}, 1000);

Execute isso com bun run websocket-client.ts. Ele se conecta ao servidor de eco, envia mensagens, as recebe de volta e então fecha.

2. Servidor WebSocket (Integração Bun.serve):

Lidar com conexões WebSocket no servidor é feito adicionando uma propriedade websocket ao objeto de configuração Bun.serve. Esta propriedade contém funções manipuladoras para diferentes eventos do ciclo de vida do WebSocket.

// websocket-server.ts
import { type ServerWebSocket, type WebSocketHandler } from "bun";

console.log("Starting WebSocket server...");

// Define o objeto manipulador WebSocket
const wsHandler: WebSocketHandler<{ authToken: string }> = {
  // open(ws): Chamado quando uma nova conexão WebSocket é estabelecida.
  // A chamada server.upgrade() em 'fetch' é necessária primeiro.
  open(ws: ServerWebSocket<{ authToken: string }>) {
    console.log(`[Server] Connection opened. Token: ${ws.data.authToken}`);
    ws.send("Welcome to the Bun WebSocket server!");
    // Assinar um tópico pub/sub
    ws.subscribe("the-group-chat");
    // Publicar uma mensagem no tópico (incluindo o novo usuário)
    ws.publish("the-group-chat", `User with token ${ws.data.authToken} joined.`);
  },

  // message(ws, message): Chamado quando uma mensagem é recebida de um cliente.
  // 'message' pode ser uma string ou um Uint8Array (Buffer).
  message(ws: ServerWebSocket<{ authToken: string }>, message: string | Buffer) {
    const messageString = message.toString(); // Lidar com string/buffer
    console.log(`[Server] Received from token ${ws.data.authToken}: ${messageString}`);

     // Ecoar a mensagem prefixada com o token
    // ws.send(`[${ws.data.authToken}] You sent: ${messageString}`);

    // Publicar a mensagem para todos na sala de chat (incluindo remetente)
    server.publish("the-group-chat", `[${ws.data.authToken}] ${messageString}`);
  },

  // close(ws, code, reason): Chamado quando uma conexão WebSocket é fechada.
  close(ws: ServerWebSocket<{ authToken: string }>, code: number, reason: string) {
    const goodbyeMessage = `[Server] Connection closed. Token: ${ws.data.authToken}, Code: ${code}, Reason: ${reason}`;
    console.log(goodbyeMessage);
     // Cancelar assinatura e notificar outros
     ws.unsubscribe("the-group-chat");
     server.publish("the-group-chat", `User with token ${ws.data.authToken} left.`);
  },

  // drain(ws): Opcional. Chamado quando a quantidade bufferizada do socket diminui,
  // indicando que está pronto para receber mais dados após contrapressão.
  // Útil para gerenciar controle de fluxo ao enviar grandes quantidades de dados.
  // drain(ws: ServerWebSocket<{ authToken: string }>) {
  //   console.log(`[Server] Drain event for token ${ws.data.authToken}`);
  // },
};

// Criar o servidor HTTP com tratamento de WebSocket
const server = Bun.serve<{ authToken: string }>({ // Passar genérico para o tipo ws.data
  port: 8081,

  // O manipulador fetch precisa lidar com a requisição de upgrade HTTP -> WebSocket
  fetch(req: Request, server: Server): Response | undefined {
    const url = new URL(req.url);
    // Verificar se a requisição está tentando fazer upgrade para WebSocket
    if (url.pathname === "/ws") {
       // Extrair alguns dados (por exemplo, token de autenticação) para passar para o contexto WebSocket
       const token = req.headers.get("sec-websocket-protocol") || "anonymous"; // Exemplo: usar cabeçalho de protocolo para token
       const success = server.upgrade(req, {
         // O objeto data é anexado à instância ws (ws.data)
         // disponível em todos os manipuladores websocket para esta conexão.
         // DEVE ser serializável em JSON se usado em workers/processos posteriormente.
         data: {
           authToken: token,
         },
         // headers: new Headers({ 'X-Custom-Response-Header': 'UpgradeValue' }) // Opcional: Adicionar cabeçalhos à resposta 101
      });

      if (success) {
        // server.upgrade() lida com a resposta 101 Switching Protocols.
        // Não retornar nada do manipulador fetch após upgrade bem-sucedido.
        return undefined;
      } else {
         // Upgrade falhou (por exemplo, cabeçalhos de requisição inválidos)
         return new Response("WebSocket upgrade failed", { status: 400 });
      }
    }

    // Lidar com requisições HTTP regulares em outros caminhos
    return new Response("Not a WebSocket endpoint. Try /ws", { status: 404 });
  },

  // Anexar o objeto manipulador WebSocket
  websocket: wsHandler,

  error(error: Error): Response | Promise<Response> {
    console.error("[Server Error]", error);
    return new Response("Server error", { status: 500 });
  },
});

console.log(`Bun WebSocket server listening on ws://localhost:${server.port}/ws`);

Conceitos Chave do Servidor:

Requisição de Upgrade: Conexões WebSocket começam como requisições HTTP GET padrão com cabeçalhos específicos (Upgrade: websocket, Connection: Upgrade, Sec-WebSocket-Key, etc.). O manipulador fetch deve detectar essas requisições.

  • server.upgrade(req, { data: ... }): Este método crucial, chamado dentro do manipulador fetch, tenta completar o handshake WebSocket. Se bem-sucedido, retorna true, e o Bun assume a conexão usando os manipuladores websocket. Se falhar, retorna false. Você deve retornar undefined de fetch após um upgrade bem-sucedido.
  • Objeto Manipulador websocket: Contém as funções open, message, close e drain para gerenciar o ciclo de vida do WebSocket para clientes conectados.
  • ServerWebSocket<T>: O tipo que representa uma conexão WebSocket do lado do servidor. O genérico T define o tipo do objeto ws.data.
  • ws.data: Um objeto associado a cada conexão, inicializado pela propriedade data nas opções de server.upgrade. Útil para armazenar estado específico da conexão (como IDs de usuário, tokens de autenticação).

Pub/Sub: O Bun inclui capacidades de publish/subscribe integradas e eficientes para WebSockets:

  • ws.subscribe("topic-name"): Assina uma conexão a um tópico.
  • ws.unsubscribe("topic-name"): Cancela a assinatura de uma conexão.
  • ws.publish("topic-name", message): Envia uma mensagem para todas as conexões inscritas naquele tópico, exceto o remetente ws.
  • server.publish("topic-name", message): Envia uma mensagem para todas as conexões inscritas no tópico (útil para transmissões em todo o sistema).

Contrapressão: O método ws.send() retorna o número de bytes bufferizados. Se este valor crescer muito, você pode estar enviando mais rápido do que o cliente pode receber. O evento drain sinaliza quando o buffer diminuiu, permitindo que você retome o envio com segurança.

O suporte integrado a WebSocket do Bun fornece uma maneira performática e conveniente de construir recursos em tempo real em aplicações, completo com funcionalidade pub/sub integrada.


Este guia cobriu os aspectos fundamentais do Bun, desde sua filosofia central e instalação até suas APIs específicas como Bun.serve, Bun.file, Bun.build e suas implementações de Padrões Web cruciais como fetch e WebSocket. Ao combinar velocidade, ferramentas integradas, suporte nativo a TypeScript/JSX e um foco em padrões, o Bun oferece um ambiente atraente e produtivo para desenvolvimento moderno de JavaScript e TypeScript.

💡
Quer uma ótima ferramenta de teste de API que gere documentação de API bonita?

Quer uma plataforma integrada, tudo-em-um, para sua equipe de desenvolvimento trabalhar em conjunto com produtividade máxima?

O Apidog entrega todas as suas demandas e substitui o Postman por um preço muito mais acessível!
button