Skip to content

Configuração Básica

index.ts
ts
const server = Bun.serve({
  // `routes` requer Bun v1.2.3+
  routes: {
    // Rotas estáticas
    "/api/status": new Response("OK"),

    // Rotas dinâmicas
    "/users/:id": req => {
      return new Response(`Olá Usuário ${req.params.id}!`);
    },

    // Handlers por método HTTP
    "/api/posts": {
      GET: () => new Response("Listar posts"),
      POST: async req => {
        const body = await req.json();
        return Response.json({ created: true, ...body });
      },
    },

    // Rota wildcard para todas as rotas que começam com "/api/" e não são correspondidas
    "/api/*": Response.json({ message: "Não encontrado" }, { status: 404 }),

    // Redirecionamento de /blog/hello para /blog/hello/world
    "/blog/hello": Response.redirect("/blog/hello/world"),

    // Serve um arquivo carregando-o preguiçosamente na memória
    "/favicon.ico": Bun.file("./favicon.ico"),
  },

  // (opcional) fallback para rotas não correspondidas:
  // Necessário se versão do Bun < 1.2.3
  fetch(req) {
    return new Response("Não Encontrado", { status: 404 });
  },
});

console.log(`Servidor rodando em ${server.url}`);

Imports de HTML

O Bun suporta importar arquivos HTML diretamente no seu código do servidor, permitindo aplicações full-stack com código tanto do lado do servidor quanto do cliente. Imports de HTML funcionam em dois modos:

Desenvolvimento (bun --hot): Assets são bundled sob demanda em runtime, habilitando hot module replacement (HMR) para uma experiência de desenvolvimento iterativa e rápida. Quando você muda seu código frontend, o navegador automaticamente atualiza sem um reload completo da página.

Produção (bun build): Ao buildar com bun build --target=bun, a declaração import index from "./index.html" resolve para um objeto de manifesto pré-buildado contendo todos os assets do cliente bundled. Bun.serve consome este manifesto para servir assets otimizados com zero overhead de bundling em runtime. Isto é ideal para deploy em produção.

ts
import myReactSinglePageApp from "./index.html";

Bun.serve({
  routes: {
    "/": myReactSinglePageApp,
  },
});

Imports de HTML não apenas servem HTML — é um bundler frontend completo, transpilador e toolkit built usando o bundler do Bun, transpilador JavaScript e parser CSS. Você pode usar isto para construir frontends completos com React, TypeScript, Tailwind CSS e mais.

Para um guia completo sobre construir aplicações full-stack com imports de HTML, incluindo exemplos detalhados e melhores práticas, veja /docs/bundler/fullstack.


Configuração

Mudando o port e hostname

Para configurar em qual porta e hostname o servidor irá escutar, defina port e hostname no objeto de opções.

ts
Bun.serve({
  port: 8080, // padrão é $BUN_PORT, $PORT, $NODE_PORT caso contrário 3000
  hostname: "mydomain.com", // padrão é "0.0.0.0"
  fetch(req) {
    return new Response("404!");
  },
});

Para selecionar aleatoriamente uma porta disponível, defina port como 0.

ts
const server = Bun.serve({
  port: 0, // porta aleatória
  fetch(req) {
    return new Response("404!");
  },
});

// server.port é a porta selecionada aleatoriamente
console.log(server.port);

Você pode visualizar a porta selecionada acessando a propriedade port no objeto server, ou acessando a propriedade url.

ts
console.log(server.port); // 3000
console.log(server.url); // http://localhost:3000

Configurando uma porta padrão

O Bun suporta várias opções e variáveis de ambiente para configurar a porta padrão. A porta padrão é usada quando a opção port não é definida.

  • Flag CLI --port
sh
bun --port=4002 server.ts
  • Variável de ambiente BUN_PORT
sh
BUN_PORT=4002 bun server.ts
  • Variável de ambiente PORT
sh
PORT=4002 bun server.ts
  • Variável de ambiente NODE_PORT
sh
NODE_PORT=4002 bun server.ts

Unix Domain Sockets

Para escutar em um unix domain socket, passe a opção unix com o caminho para o socket.

ts
Bun.serve({
  unix: "/tmp/my-socket.sock", // caminho para o socket
  fetch(req) {
    return new Response(`404!`);
  },
});

Abstract Namespace Sockets

O Bun suporta abstract namespace sockets do Linux. Para usar um abstract namespace socket, prefixe o caminho unix com um null byte.

ts
Bun.serve({
  unix: "\0my-abstract-socket", // abstract namespace socket
  fetch(req) {
    return new Response(`404!`);
  },
});

Ao contrário de unix domain sockets, abstract namespace sockets não são bound ao filesystem e são automaticamente removidos quando a última referência ao socket é fechada.


idleTimeout

Para configurar o idle timeout, defina o campo idleTimeout no Bun.serve.

ts
Bun.serve({
  // 10 segundos:
  idleTimeout: 10,

  fetch(req) {
    return new Response("Bun!");
  },
});

Este é o máximo de tempo que uma conexão é permitida ficar idle antes que o servidor a feche. Uma conexão está idling se não há dados enviados ou recebidos.


Sintaxe export default

Até agora, os exemplos nesta página usaram a API explícita Bun.serve. O Bun também suporta uma sintaxe alternativa.

ts
import { type Serve } from "bun";

export default {
  fetch(req) {
    return new Response("Bun!");
  },
} satisfies Serve;

Ao invés de passar as opções do server para Bun.serve, faça export default. Este arquivo pode ser executado como está; quando o Bun vê um arquivo com um export default contendo um handler fetch, ele passa para Bun.serve internamente.


Hot Reloading de Rotas

Atualize rotas sem restarts do servidor usando server.reload():

ts
const server = Bun.serve({
  routes: {
    "/api/version": () => Response.json({ version: "1.0.0" }),
  },
});

// Deploy de novas rotas sem downtime
server.reload({
  routes: {
    "/api/version": () => Response.json({ version: "2.0.0" }),
  },
});

Métodos de Lifecycle do Servidor

server.stop()

Para parar o servidor de aceitar novas conexões:

ts
const server = Bun.serve({
  fetch(req) {
    return new Response("Olá!");
  },
});

// Para graciosamente o servidor (aguarda requisições em andamento)
await server.stop();

// Força parada e fecha todas as conexões ativas
await server.stop(true);

Por padrão, stop() permite que requisições em andamento e conexões WebSocket completem. Passe true para imediatamente terminar todas as conexões.

server.ref() e server.unref()

Controle se o servidor mantém o processo Bun vivo:

ts
// Não mantém processo vivo se servidor é a única coisa rodando
server.unref();

// Restaura comportamento padrão - mantém processo vivo
server.ref();

server.reload()

Atualiza handlers do servidor sem restart:

ts
const server = Bun.serve({
  routes: {
    "/api/version": Response.json({ version: "v1" }),
  },
  fetch(req) {
    return new Response("v1");
  },
});

// Atualiza para novo handler
server.reload({
  routes: {
    "/api/version": Response.json({ version: "v2" }),
  },
  fetch(req) {
    return new Response("v2");
  },
});

Isto é útil para desenvolvimento e hot reloading. Apenas fetch, error, e routes podem ser atualizados.


Controles Por-Requisição

server.timeout(Request, seconds)

Define um idle timeout customizado para requisições individuais:

ts
const server = Bun.serve({
  async fetch(req, server) {
    // Define timeout de 60 segundos para esta requisição
    server.timeout(req, 60);

    // Se demorarem mais que 60 segundos para enviar o body, a requisição será abortada
    await req.text();

    return new Response("Feito!");
  },
});

Passe 0 para desabilitar o timeout para uma requisição.

server.requestIP(Request)

Obtém informações de IP e porta do cliente:

ts
const server = Bun.serve({
  fetch(req, server) {
    const address = server.requestIP(req);
    if (address) {
      return new Response(`IP do Cliente: ${address.address}, Porta: ${address.port}`);
    }
    return new Response("Cliente desconhecido");
  },
});

Retorna null para requisições fechadas ou Unix domain sockets.


Métricas do Servidor

server.pendingRequests e server.pendingWebSockets

Monitore a atividade do servidor com contadores built-in:

ts
const server = Bun.serve({
  fetch(req, server) {
    return new Response(
      `Requisições ativas: ${server.pendingRequests}\n` + `WebSockets ativas: ${server.pendingWebSockets}`,
    );
  },
});

server.subscriberCount(topic)

Obtenha a contagem de assinantes para um tópico WebSocket:

ts
const server = Bun.serve({
  fetch(req, server) {
    const chatUsers = server.subscriberCount("chat");
    return new Response(`${chatUsers} usuários no chat`);
  },
  websocket: {
    message(ws) {
      ws.subscribe("chat");
    },
  },
});

Benchmarks

Abaixo estão implementações do Bun e Node.js de um servidor HTTP simples que responde Bun! para cada Request recebida.

ts
Bun.serve({
  fetch(req: Request) {
    return new Response("Bun!");
  },
  port: 3000,
});
ts
require("http")
  .createServer((req, res) => res.end("Bun!"))
  .listen(8080);

O servidor Bun.serve pode lidar com aproximadamente 2.5x mais requisições por segundo que o Node.js no Linux.

RuntimeRequisições por segundo
Node 16~64,000
Bun~160,000

Exemplo Prático: REST API

Aqui está uma REST API básica baseada em banco de dados usando o router do Bun com zero dependências:

ts
import type { Post } from "./types.ts";
import { Database } from "bun:sqlite";

const db = new Database("posts.db");
db.exec(`
  CREATE TABLE IF NOT EXISTS posts (
    id TEXT PRIMARY KEY,
    title TEXT NOT NULL,
    content TEXT NOT NULL,
    created_at TEXT NOT NULL
  )
`);

Bun.serve({
  routes: {
    // Listar posts
    "/api/posts": {
      GET: () => {
        const posts = db.query("SELECT * FROM posts").all();
        return Response.json(posts);
      },

      // Criar post
      POST: async req => {
        const post: Omit<Post, "id" | "created_at"> = await req.json();
        const id = crypto.randomUUID();

        db.query(
          `INSERT INTO posts (id, title, content, created_at)
           VALUES (?, ?, ?, ?)`,
        ).run(id, post.title, post.content, new Date().toISOString());

        return Response.json({ id, ...post }, { status: 201 });
      },
    },

    // Obter post por ID
    "/api/posts/:id": req => {
      const post = db.query("SELECT * FROM posts WHERE id = ?").get(req.params.id);

      if (!post) {
        return new Response("Não Encontrado", { status: 404 });
      }

      return Response.json(post);
    },
  },

  error(error) {
    console.error(error);
    return new Response("Erro Interno do Servidor", { status: 500 });
  },
});
ts
export interface Post {
  id: string;
  title: string;
  content: string;
  created_at: string;
}

Referência

ts
interface Server extends Disposable {
  /**
   * Para o servidor de aceitar novas conexões.
   * @param closeActiveConnections Se true, imediatamente termina todas as conexões
   * @returns Promise que resolve quando o servidor parou
   */
  stop(closeActiveConnections?: boolean): Promise<void>;

  /**
   * Atualiza handlers sem restartar o servidor.
   * Apenas handlers fetch e error podem ser atualizados.
   */
  reload(options: Serve): void;

  /**
   * Faz uma requisição para o servidor em execução.
   * Útil para testes ou roteamento interno.
   */
  fetch(request: Request | string): Response | Promise<Response>;

  /**
   * Upgrade de uma requisição HTTP para uma conexão WebSocket.
   * @returns true se upgrade bem sucedido, false se falhou
   */
  upgrade<T = undefined>(
    request: Request,
    options?: {
      headers?: Bun.HeadersInit;
      data?: T;
    },
  ): boolean;

  /**
   * Publica uma mensagem para todos os clientes WebSocket assinados em um tópico.
   * @returns Bytes enviados, 0 se dropped, -1 se backpressure aplicado
   */
  publish(
    topic: string,
    data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer,
    compress?: boolean,
  ): ServerWebSocketSendStatus;

  /**
   * Obtém contagem de clientes WebSocket assinados em um tópico.
   */
  subscriberCount(topic: string): number;

  /**
   * Obtém endereço IP e porta do cliente.
   * @returns null para requisições fechadas ou Unix sockets
   */
  requestIP(request: Request): SocketAddress | null;

  /**
   * Define idle timeout customizado para uma requisição.
   * @param seconds Timeout em segundos, 0 para desabilitar
   */
  timeout(request: Request, seconds: number): void;

  /**
   * Mantém processo vivo enquanto servidor está rodando.
   */
  ref(): void;

  /**
   * Permite processo sair se servidor é a única coisa rodando.
   */
  unref(): void;

  /** Número de requisições HTTP em andamento */
  readonly pendingRequests: number;

  /** Número de conexões WebSocket ativas */
  readonly pendingWebSockets: number;

  /** URL do servidor incluindo protocolo, hostname e porta */
  readonly url: URL;

  /** Porta que o servidor está escutando */
  readonly port: number;

  /** Hostname que o servidor está bound */
  readonly hostname: string;

  /** Se servidor está em modo de desenvolvimento */
  readonly development: boolean;

  /** Identificador da instância do servidor */
  readonly id: string;
}

interface WebSocketHandler<T = undefined> {
  /** Tamanho máximo de mensagem WebSocket em bytes */
  maxPayloadLength?: number;

  /** Bytes de mensagens enfileiradas antes de aplicar backpressure */
  backpressureLimit?: number;

  /** Se deve fechar conexão quando limite de backpressure atingido */
  closeOnBackpressureLimit?: boolean;

  /** Chamado quando backpressure é aliviado */
  drain?(ws: ServerWebSocket<T>): void | Promise<void>;

  /** Segundos antes do idle timeout */
  idleTimeout?: number;

  /** Habilita compressão per-message deflate */
  perMessageDeflate?:
    | boolean
    | {
        compress?: WebSocketCompressor | boolean;
        decompress?: WebSocketCompressor | boolean;
      };

  /** Envia frames ping para manter conexão viva */
  sendPings?: boolean;

  /** Se servidor recebe suas próprias mensagens publicadas */
  publishToSelf?: boolean;

  /** Chamado quando conexão aberta */
  open?(ws: ServerWebSocket<T>): void | Promise<void>;

  /** Chamado quando mensagem recebida */
  message(ws: ServerWebSocket<T>, message: string | Buffer): void | Promise<void>;

  /** Chamado quando conexão fechada */
  close?(ws: ServerWebSocket<T>, code: number, reason: string): void | Promise<void>;

  /** Chamado quando frame ping recebido */
  ping?(ws: ServerWebSocket<T>, data: Buffer): void | Promise<void>;

  /** Chamado quando frame pong recebido */
  pong?(ws: ServerWebSocket<T>, data: Buffer): void | Promise<void>;
}

interface TLSOptions {
  /** Certificate authority chain */
  ca?: string | Buffer | BunFile | Array<string | Buffer | BunFile>;

  /** Server certificate */
  cert?: string | Buffer | BunFile | Array<string | Buffer | BunFile>;

  /** Caminho para arquivo de parâmetros DH */
  dhParamsFile?: string;

  /** Private key */
  key?: string | Buffer | BunFile | Array<string | Buffer | BunFile>;

  /** Reduz uso de memória TLS */
  lowMemoryMode?: boolean;

  /** Passphrase da private key */
  passphrase?: string;

  /** Flags de opções OpenSSL */
  secureOptions?: number;

  /** Server name para SNI */
  serverName?: string;
}

Bun by www.bunjs.com.cn edit