Começando com a API Bun

Mark Ponomarev

Mark Ponomarev

18 julho 2025

Começando com a API Bun

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 install -g bun
docker run --rm --init --ulimit memlock=-1:-1 oven/bun:latest bun --version
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:

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:

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:

API de URL:

API de Streams:

API de Codificação:

API de Blob:

API de FormData:

API de WebSocket:

Timers:

API de Console:

API de Criptografia:

API 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:

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:

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

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'.

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.

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

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

Pratique o design de API no Apidog

Descubra uma forma mais fácil de construir e usar APIs