Skip to content

Configurazione Base

index.ts
ts
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.

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

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

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

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

Configurare 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
sh
bun --port=4002 server.ts
  • Variabile d'ambiente BUN_PORT
sh
BUN_PORT=4002 bun server.ts
  • Variabile d'ambiente PORT
sh
PORT=4002 bun server.ts
  • Variabile d'ambiente NODE_PORT
sh
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.

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

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

ts
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():

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

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

ts
// 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:

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

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

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

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

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

ts
Bun.serve({
  fetch(req: Request) {
    return new Response("Bun!");
  },
  port: 3000,
});
ts
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.

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

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: {
    // 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 });
  },
});
ts
export interface Post {
  id: string;
  title: string;
  content: string;
  created_at: string;
}

Riferimento

ts
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;
}

Bun a cura di www.bunjs.com.cn