Configuração Básica
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.
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.
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.
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.
console.log(server.port); // 3000
console.log(server.url); // http://localhost:3000Configurando 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
bun --port=4002 server.ts- Variável de ambiente
BUN_PORT
BUN_PORT=4002 bun server.ts- Variável de ambiente
PORT
PORT=4002 bun server.ts- Variável de ambiente
NODE_PORT
NODE_PORT=4002 bun server.tsUnix Domain Sockets
Para escutar em um unix domain socket, passe a opção unix com o caminho para o socket.
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.
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.
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.
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():
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:
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:
// 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:
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:
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:
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:
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:
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.
Bun.serve({
fetch(req: Request) {
return new Response("Bun!");
},
port: 3000,
});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.
| Runtime | Requisiçõ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:
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 });
},
});export interface Post {
id: string;
title: string;
content: string;
created_at: string;
}Referência
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;
}