Configurazione Base
const server = Bun.serve({
// `routes` richiede Bun v1.2.3+
routes: {
// Route statiche
"/api/status": new Response("OK"),
// Route dinamiche
"/users/:id": req => {
return new Response(`Ciao Utente ${req.params.id}!`);
},
// Gestori per metodo HTTP
"/api/posts": {
GET: () => new Response("Lista post"),
POST: async req => {
const body = await req.json();
return Response.json({ created: true, ...body });
},
},
// Route wildcard per tutte le route che iniziano con "/api/" e non sono altrimenti corrispondenti
"/api/*": Response.json({ message: "Non trovato" }, { status: 404 }),
// Reindirizza da /blog/hello a /blog/hello/world
"/blog/hello": Response.redirect("/blog/hello/world"),
// Servi un file caricandolo lazy in memoria
"/favicon.ico": Bun.file("./favicon.ico"),
},
// (opzionale) fallback per route non corrispondenti:
// Richiesto se la versione di Bun < 1.2.3
fetch(req) {
return new Response("Non Trovato", { status: 404 });
},
});
console.log(`Server in esecuzione su ${server.url}`);Importazioni HTML
Bun supporta l'importazione diretta di file HTML nel codice del server, abilitando applicazioni full-stack con codice sia lato server che lato client. Le importazioni HTML funzionano in due modalità:
Sviluppo (bun --hot): Gli asset sono bundlati on-demand a runtime, abilitando l'hot module replacement (HMR) per un'esperienza di sviluppo iterativa e veloce. Quando modifichi il codice frontend, il browser si aggiorna automaticamente senza un ricaricamento completo della pagina.
Produzione (bun build): Quando esegui il build con bun build --target=bun, l'istruzione import index from "./index.html" si risolve in un oggetto manifest pre-built contenente tutti gli asset client bundlati. Bun.serve consuma questo manifest per servire asset ottimizzati con zero overhead di bundling a runtime. Questo è ideale per il deploy in produzione.
import myReactSinglePageApp from "./index.html";
Bun.serve({
routes: {
"/": myReactSinglePageApp,
},
});Le importazioni HTML non servono solo HTML - è un bundler frontend completo, transpiler e toolkit costruito usando il bundler di Bun, il transpiler JavaScript e il parser CSS. Puoi usare questo per costruire frontend completi con React, TypeScript, Tailwind CSS e altro.
Per una guida completa sulla costruzione di applicazioni full-stack con importazioni HTML, inclusi esempi dettagliati e best practices, vedi /it/docs/bundler/fullstack.
Configurazione
Cambiare la port e l'hostname
Per configurare su quale porta e hostname il server rimarrà in ascolto, imposta port e hostname nell'oggetto delle opzioni.
Bun.serve({
port: 8080, // predefinito a $BUN_PORT, $PORT, $NODE_PORT altrimenti 3000
hostname: "mydomain.com", // predefinito a "0.0.0.0"
fetch(req) {
return new Response("404!");
},
});Per selezionare casualmente una porta disponibile, imposta port a 0.
const server = Bun.serve({
port: 0, // porta casuale
fetch(req) {
return new Response("404!");
},
});
// server.port è la porta selezionata casualmente
console.log(server.port);Puoi vedere la porta scelta accedendo alla proprietà port sull'oggetto server, o accedendo alla proprietà url.
console.log(server.port); // 3000
console.log(server.url); // http://localhost:3000Configurare una porta predefinita
Bun supporta diverse opzioni e variabili d'ambiente per configurare la porta predefinita. La porta predefinita viene usata quando l'opzione port non è impostata.
- Flag CLI
--port
bun --port=4002 server.ts- Variabile d'ambiente
BUN_PORT
BUN_PORT=4002 bun server.ts- Variabile d'ambiente
PORT
PORT=4002 bun server.ts- Variabile d'ambiente
NODE_PORT
NODE_PORT=4002 bun server.ts
---
## Socket di dominio Unix
Per ascoltare su un [socket di dominio Unix](https://en.wikipedia.org/wiki/Unix_domain_socket), passa l'opzione `unix` con il percorso al socket.
```ts
Bun.serve({
unix: "/tmp/my-socket.sock", // percorso al socket
fetch(req) {
return new Response(`404!`);
},
});Socket del namespace astratto
Bun supporta i socket del namespace astratto Linux. Per usare un socket del namespace astratto, prefissa il percorso unix con un byte nullo.
Bun.serve({
unix: "\0my-abstract-socket", // socket del namespace astratto
fetch(req) {
return new Response(`404!`);
},
});A differenza dei socket di dominio Unix, i socket del namespace astratto non sono legati al filesystem e vengono automaticamente rimossi quando l'ultimo riferimento al socket viene chiuso.
idleTimeout
Per configurare il timeout di inattività, imposta il campo idleTimeout in Bun.serve.
Bun.serve({
// 10 secondi:
idleTimeout: 10,
fetch(req) {
return new Response("Bun!");
},
});Questo è il tempo massimo consentito a una connessione di essere inattiva prima che il server la chiuda. Una connessione è inattiva se non ci sono dati inviati o ricevuti.
Sintassi export default
Finora, gli esempi in questa pagina hanno usato l'API esplicita Bun.serve. Bun supporta anche una sintassi alternativa.
import { type Serve } from "bun";
export default {
fetch(req) {
return new Response("Bun!");
},
} satisfies Serve;Invece di passare le opzioni del server in Bun.serve, esportale con export default. Questo file può essere eseguito così com'è; quando Bun vede un file con un export default contenente un gestore fetch, lo passa a Bun.serve internamente.
Hot Route Reloading
Aggiorna le route senza riavviare il server usando server.reload():
const server = Bun.serve({
routes: {
"/api/version": () => Response.json({ version: "1.0.0" }),
},
});
// Distribuisci nuove route senza downtime
server.reload({
routes: {
"/api/version": () => Response.json({ version: "2.0.0" }),
},
});Metodi del Ciclo di Vita del Server
server.stop()
Per fermare il server dall'accettare nuove connessioni:
const server = Bun.serve({
fetch(req) {
return new Response("Ciao!");
},
});
// Ferma il server gracefully (aspetta le richieste in-flight)
await server.stop();
// Ferma forzatamente e chiudi tutte le connessioni attive
await server.stop(true);Di default, stop() permette alle richieste e alle connessioni WebSocket in-flight di completarsi. Passa true per terminare immediatamente tutte le connessioni.
server.ref() e server.unref()
Controlla se il server mantiene il processo Bun alive:
// Non mantenere il processo alive se il server è l'unica cosa in esecuzione
server.unref();
// Ripristina il comportamento predefinito - mantieni il processo alive
server.ref();server.reload()
Aggiorna i gestori del server senza riavviare:
const server = Bun.serve({
routes: {
"/api/version": Response.json({ version: "v1" }),
},
fetch(req) {
return new Response("v1");
},
});
// Aggiorna al nuovo gestore
server.reload({
routes: {
"/api/version": Response.json({ version: "v2" }),
},
fetch(req) {
return new Response("v2");
},
});Questo è utile per lo sviluppo e l'hot reloading. Solo fetch, error e routes possono essere aggiornati.
Controlli per Richiesta
server.timeout(Request, seconds)
Imposta un timeout di inattività personalizzato per singole richieste:
const server = Bun.serve({
async fetch(req, server) {
// Imposta timeout di 60 secondi per questa richiesta
server.timeout(req, 60);
// Se impiegano più di 60 secondi per inviare il body, la richiesta sarà abortita
await req.text();
return new Response("Fatto!");
},
});Passa 0 per disabilitare il timeout per una richiesta.
server.requestIP(Request)
Ottieni informazioni IP e porta del client:
const server = Bun.serve({
fetch(req, server) {
const address = server.requestIP(req);
if (address) {
return new Response(`IP Client: ${address.address}, Porta: ${address.port}`);
}
return new Response("Client sconosciuto");
},
});Restituisce null per richieste chiuse o socket di dominio Unix.
Metriche del Server
server.pendingRequests e server.pendingWebSockets
Monitora l'attività del server con contatori integrati:
const server = Bun.serve({
fetch(req, server) {
return new Response(
`Richieste attive: ${server.pendingRequests}\n` + `WebSocket attivi: ${server.pendingWebSockets}`,
);
},
});server.subscriberCount(topic)
Ottieni il conteggio degli iscritti per un topic WebSocket:
const server = Bun.serve({
fetch(req, server) {
const chatUsers = server.subscriberCount("chat");
return new Response(`${chatUsers} utenti in chat`);
},
websocket: {
message(ws) {
ws.subscribe("chat");
},
},
});Benchmark
Qui sotto ci sono implementazioni Bun e Node.js di un semplice server HTTP che risponde Bun! a ogni Request in ingresso.
Bun.serve({
fetch(req: Request) {
return new Response("Bun!");
},
port: 3000,
});require("http")
.createServer((req, res) => res.end("Bun!"))
.listen(8080);Il server Bun.serve può gestire circa 2,5 volte più richieste al secondo rispetto a Node.js su Linux.
| Runtime | Richieste al secondo |
|---|---|
| Node 16 | ~64.000 |
| Bun | ~160.000 |
Esempio pratico: REST API
Ecco una REST API basic con database usando il router di Bun con zero dipendenze:
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: {
// Lista post
"/api/posts": {
GET: () => {
const posts = db.query("SELECT * FROM posts").all();
return Response.json(posts);
},
// Crea 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 });
},
},
// Ottieni post per ID
"/api/posts/:id": req => {
const post = db.query("SELECT * FROM posts WHERE id = ?").get(req.params.id);
if (!post) {
return new Response("Non Trovato", { status: 404 });
}
return Response.json(post);
},
},
error(error) {
console.error(error);
return new Response("Errore Server Interno", { status: 500 });
},
});export interface Post {
id: string;
title: string;
content: string;
created_at: string;
}Riferimento
interface Server extends Disposable {
/**
* Ferma il server dall'accettare nuove connessioni.
* @param closeActiveConnections Se true, termina immediatamente tutte le connessioni
* @returns Promise che si risolve quando il server si è fermato
*/
stop(closeActiveConnections?: boolean): Promise<void>;
/**
* Aggiorna i gestori senza riavviare il server.
* Solo i gestori fetch e error possono essere aggiornati.
*/
reload(options: Serve): void;
/**
* Fai una richiesta al server in esecuzione.
* Utile per test o routing interno.
*/
fetch(request: Request | string): Response | Promise<Response>;
/**
* Aggiorna una richiesta HTTP a una connessione WebSocket.
* @returns true se l'upgrade ha avuto successo, false se fallito
*/
upgrade<T = undefined>(
request: Request,
options?: {
headers?: Bun.HeadersInit;
data?: T;
},
): boolean;
/**
* Pubblica un messaggio a tutti i client WebSocket iscritti a un topic.
* @returns Byte inviati, 0 se scartati, -1 se applicata backpressure
*/
publish(
topic: string,
data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer,
compress?: boolean,
): ServerWebSocketSendStatus;
/**
* Ottieni il conteggio dei client WebSocket iscritti a un topic.
*/
subscriberCount(topic: string): number;
/**
* Ottieni indirizzo IP e porta del client.
* @returns null per richieste chiuse o socket Unix
*/
requestIP(request: Request): SocketAddress | null;
/**
* Imposta timeout di inattività personalizzato per una richiesta.
* @param seconds Timeout in secondi, 0 per disabilitare
*/
timeout(request: Request, seconds: number): void;
/**
* Mantieni il processo alive mentre il server è in esecuzione.
*/
ref(): void;
/**
* Permetti al processo di uscire se il server è l'unica cosa in esecuzione.
*/
unref(): void;
/** Numero di richieste HTTP in-flight */
readonly pendingRequests: number;
/** Numero di connessioni WebSocket attive */
readonly pendingWebSockets: number;
/** URL del server inclusi protocollo, hostname e porta */
readonly url: URL;
/** Porta su cui il server è in ascolto */
readonly port: number;
/** Hostname a cui il server è legato */
readonly hostname: string;
/** Se il server è in modalità sviluppo */
readonly development: boolean;
/** Identificatore dell'istanza del server */
readonly id: string;
}
interface WebSocketHandler<T = undefined> {
/** Dimensione massima del messaggio WebSocket in byte */
maxPayloadLength?: number;
/** Byte di messaggi in coda prima di applicare backpressure */
backpressureLimit?: number;
/** Se chiudere la connessione quando viene raggiunto il limite di backpressure */
closeOnBackpressureLimit?: boolean;
/** Chiamato quando la backpressure viene alleviata */
drain?(ws: ServerWebSocket<T>): void | Promise<void>;
/** Secondi prima del timeout di inattività */
idleTimeout?: number;
/** Abilita la compressione deflate per messaggio */
perMessageDeflate?:
| boolean
| {
compress?: WebSocketCompressor | boolean;
decompress?: WebSocketCompressor | boolean;
};
/** Invia frame ping per mantenere la connessione alive */
sendPings?: boolean;
/** Se il server riceve i propri messaggi pubblicati */
publishToSelf?: boolean;
/** Chiamato quando la connessione si apre */
open?(ws: ServerWebSocket<T>): void | Promise<void>;
/** Chiamato quando viene ricevuto un messaggio */
message(ws: ServerWebSocket<T>, message: string | Buffer): void | Promise<void>;
/** Chiamato quando la connessione si chiude */
close?(ws: ServerWebSocket<T>, code: number, reason: string): void | Promise<void>;
/** Chiamato quando viene ricevuto un frame ping */
ping?(ws: ServerWebSocket<T>, data: Buffer): void | Promise<void>;
/** Chiamato quando viene ricevuto un frame pong */
pong?(ws: ServerWebSocket<T>, data: Buffer): void | Promise<void>;
}
interface TLSOptions {
/** Catena dell'autorità di certificazione */
ca?: string | Buffer | BunFile | Array<string | Buffer | BunFile>;
/** Certificato del server */
cert?: string | Buffer | BunFile | Array<string | Buffer | BunFile>;
/** Percorso al file dei parametri DH */
dhParamsFile?: string;
/** Chiave privata */
key?: string | Buffer | BunFile | Array<string | Buffer | BunFile>;
/** Riduci uso memoria TLS */
lowMemoryMode?: boolean;
/** Passphrase della chiave privata */
passphrase?: string;
/** Flag opzioni OpenSSL */
secureOptions?: number;
/** Nome server per SNI */
serverName?: string;
}