Configuration de base
const server = Bun.serve({
// `routes` nécessite Bun v1.2.3+
routes: {
// Routes statiques
"/api/status": new Response("OK"),
// Routes dynamiques
"/users/:id": req => {
return new Response(`Bonjour Utilisateur ${req.params.id} !`);
},
// Gestionnaires par méthode HTTP
"/api/posts": {
GET: () => new Response("Lister les publications"),
POST: async req => {
const body = await req.json();
return Response.json({ created: true, ...body });
},
},
// Route joker pour toutes les routes commençant par "/api/" qui ne sont pas autrement correspondues
"/api/*": Response.json({ message: "Non trouvé" }, { status: 404 }),
// Redirection de /blog/hello vers /blog/hello/world
"/blog/hello": Response.redirect("/blog/hello/world"),
// Servir un fichier en le chargeant paresseusement en mémoire
"/favicon.ico": Bun.file("./favicon.ico"),
},
// (optionnel) repli pour les routes non correspondantes :
// Requis si la version de Bun < 1.2.3
fetch(req) {
return new Response("Non trouvé", { status: 404 });
},
});
console.log(`Serveur en cours d'exécution à ${server.url}`);Imports HTML
Bun prend en charge l'importation de fichiers HTML directement dans votre code serveur, permettant des applications full-stack avec du code côté serveur et côté client. Les imports HTML fonctionnent en deux modes :
Développement (bun --hot) : Les ressources sont regroupées à la demande à l'exécution, permettant le remplacement de module à chaud (HMR) pour une expérience de développement itérative rapide. Lorsque vous modifiez votre code frontend, le navigateur se met automatiquement à jour sans rechargement complet de la page.
Production (bun build) : Lors de la construction avec bun build --target=bun, l'instruction import index from "./index.html" se résout en un objet manifeste pré-construit contenant toutes les ressources client regroupées. Bun.serve consomme ce manifeste pour servir des ressources optimisées sans frais généraux de regroupement à l'exécution. C'est idéal pour le déploiement en production.
import myReactSinglePageApp from "./index.html";
Bun.serve({
routes: {
"/": myReactSinglePageApp,
},
});Les imports HTML ne se contentent pas de servir du HTML — c'est un bundleur frontend complet, un transpileur et une boîte à outils intégrés utilisant le bundleur, le transpileur JavaScript et l'analyseur CSS de Bun. Vous pouvez l'utiliser pour créer des frontends complets avec React, TypeScript, Tailwind CSS, et plus encore.
Pour un guide complet sur la création d'applications full-stack avec des imports HTML, y compris des exemples détaillés et les meilleures pratiques, consultez /docs/bundler/fullstack.
Configuration
Changer le port et le hostname
Pour configurer le port et le nom d'hôte sur lesquels le serveur écoutera, définissez port et hostname dans l'objet d'options.
Bun.serve({
port: 8080, // par défaut $BUN_PORT, $PORT, $NODE_PORT sinon 3000
hostname: "mydomain.com", // par défaut "0.0.0.0"
fetch(req) {
return new Response("404 !");
},
});Pour sélectionner aléatoirement un port disponible, définissez port à 0.
const server = Bun.serve({
port: 0, // port aléatoire
fetch(req) {
return new Response("404 !");
},
});
// server.port est le port sélectionné aléatoirement
console.log(server.port);Vous pouvez afficher le port choisi en accédant à la propriété port sur l'objet serveur, ou en accédant à la propriété url.
console.log(server.port); // 3000
console.log(server.url); // http://localhost:3000Configuration d'un port par défaut
Bun prend en charge plusieurs options et variables d'environnement pour configurer le port par défaut. Le port par défaut est utilisé lorsque l'option port n'est pas définie.
- Indicateur CLI
--port
bun --port=4002 server.ts- Variable d'environnement
BUN_PORT
BUN_PORT=4002 bun server.ts- Variable d'environnement
PORT
PORT=4002 bun server.ts- Variable d'environnement
NODE_PORT
NODE_PORT=4002 bun server.tsSockets de domaine Unix
Pour écouter sur un socket de domaine Unix, passez l'option unix avec le chemin du socket.
Bun.serve({
unix: "/tmp/my-socket.sock", // chemin du socket
fetch(req) {
return new Response(`404 !`);
},
});Sockets d'espace de noms abstrait
Bun prend en charge les sockets d'espace de noms abstrait Linux. Pour utiliser un socket d'espace de noms abstrait, préfixez le chemin unix avec un octet nul.
Bun.serve({
unix: "\0my-abstract-socket", // socket d'espace de noms abstrait
fetch(req) {
return new Response(`404 !`);
},
});Contrairement aux sockets de domaine Unix, les sockets d'espace de noms abstrait ne sont pas liés au système de fichiers et sont automatiquement supprimés lorsque la dernière référence au socket est fermée.
idleTimeout
Pour configurer le délai d'attente d'inactivité, définissez le champ idleTimeout dans Bun.serve.
Bun.serve({
// 10 secondes :
idleTimeout: 10,
fetch(req) {
return new Response("Bun !");
},
});C'est la durée maximale pendant laquelle une connexion est autorisée à être inactive avant que le serveur ne la ferme. Une connexion est inactive s'il n'y a aucune donnée envoyée ou reçue.
Syntaxe export default
Jusqu'à présent, les exemples sur cette page ont utilisé l'API explicite Bun.serve. Bun prend également en charge une syntaxe alternative.
import { type Serve } from "bun";
export default {
fetch(req) {
return new Response("Bun !");
},
} satisfies Serve;Au lieu de passer les options du serveur dans Bun.serve, utilisez export default. Ce fichier peut être exécuté tel quel ; lorsque Bun voit un fichier avec un export default contenant un gestionnaire fetch, il le passe dans Bun.serve en interne.
Rechargement à chaud des routes
Mettez à jour les routes sans redémarrage du serveur en utilisant server.reload() :
const server = Bun.serve({
routes: {
"/api/version": () => Response.json({ version: "1.0.0" }),
},
});
// Déployer de nouvelles routes sans temps d'arrêt
server.reload({
routes: {
"/api/version": () => Response.json({ version: "2.0.0" }),
},
});Méthodes de cycle de vie du serveur
server.stop()
Pour arrêter le serveur d'accepter de nouvelles connexions :
const server = Bun.serve({
fetch(req) {
return new Response("Bonjour !");
},
});
// Arrêter gracieusement le serveur (attend les requêtes en cours)
await server.stop();
// Arrêt forcé et fermeture de toutes les connexions actives
await server.stop(true);Par défaut, stop() permet aux requêtes en cours et aux connexions WebSocket de se terminer. Passez true pour terminer immédiatement toutes les connexions.
server.ref() et server.unref()
Contrôler si le serveur maintient le processus Bun en vie :
// Ne pas maintenir le processus en vie si le serveur est la seule chose en cours d'exécution
server.unref();
// Restaurer le comportement par défaut - maintenir le processus en vie
server.ref();server.reload()
Mettre à jour les gestionnaires du serveur sans redémarrer :
const server = Bun.serve({
routes: {
"/api/version": Response.json({ version: "v1" }),
},
fetch(req) {
return new Response("v1");
},
});
// Mettre à jour vers un nouveau gestionnaire
server.reload({
routes: {
"/api/version": Response.json({ version: "v2" }),
},
fetch(req) {
return new Response("v2");
},
});Ceci est utile pour le développement et le rechargement à chaud. Seuls fetch, error et routes peuvent être mis à jour.
Contrôles par requête
server.timeout(Request, seconds)
Définir un délai d'attente d'inactivité personnalisé pour des requêtes individuelles :
const server = Bun.serve({
async fetch(req, server) {
// Définir un délai d'attente de 60 secondes pour cette requête
server.timeout(req, 60);
// S'ils prennent plus de 60 secondes pour envoyer le corps, la requête sera abandonnée
await req.text();
return new Response("Terminé !");
},
});Passez 0 pour désactiver le délai d'attente pour une requête.
server.requestIP(Request)
Obtenir les informations d'adresse IP et de port du client :
const server = Bun.serve({
fetch(req, server) {
const address = server.requestIP(req);
if (address) {
return new Response(`IP du client : ${address.address}, Port : ${address.port}`);
}
return new Response("Client inconnu");
},
});Retourne null pour les requêtes fermées ou les sockets de domaine Unix.
Métriques du serveur
server.pendingRequests et server.pendingWebSockets
Surveiller l'activité du serveur avec des compteurs intégrés :
const server = Bun.serve({
fetch(req, server) {
return new Response(
`Requêtes actives : ${server.pendingRequests}\n` + `WebSockets actives : ${server.pendingWebSockets}`,
);
},
});server.subscriberCount(topic)
Obtenir le nombre d'abonnés pour un sujet WebSocket :
const server = Bun.serve({
fetch(req, server) {
const chatUsers = server.subscriberCount("chat");
return new Response(`${chatUsers} utilisateurs dans le chat`);
},
websocket: {
message(ws) {
ws.subscribe("chat");
},
},
});Benchmarks
Ci-dessous sont les implémentations Bun et Node.js d'un serveur HTTP simple qui répond Bun ! à chaque Request entrante.
Bun.serve({
fetch(req: Request) {
return new Response("Bun !");
},
port: 3000,
});require("http")
.createServer((req, res) => res.end("Bun !"))
.listen(8080);Le serveur Bun.serve peut gérer environ 2,5 fois plus de requêtes par seconde que Node.js sur Linux.
| Runtime | Requêtes par seconde |
|---|---|
| Node 16 | ~64 000 |
| Bun | ~160 000 |
Exemple pratique : API REST
Voici une API REST de base sauvegardée par une base de données utilisant le routeur de Bun avec zéro dépendance :
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: {
// Lister les publications
"/api/posts": {
GET: () => {
const posts = db.query("SELECT * FROM posts").all();
return Response.json(posts);
},
// Créer une publication
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 });
},
},
// Obtenir une publication par ID
"/api/posts/:id": req => {
const post = db.query("SELECT * FROM posts WHERE id = ?").get(req.params.id);
if (!post) {
return new Response("Non trouvé", { status: 404 });
}
return Response.json(post);
},
},
error(error) {
console.error(error);
return new Response("Erreur interne du serveur", { status: 500 });
},
});export interface Post {
id: string;
title: string;
content: string;
created_at: string;
}Référence
interface Server extends Disposable {
/**
* Arrêter le serveur d'accepter de nouvelles connexions.
* @param closeActiveConnections Si vrai, termine immédiatement toutes les connexions
* @returns Promise qui se résout lorsque le serveur s'est arrêté
*/
stop(closeActiveConnections?: boolean): Promise<void>;
/**
* Mettre à jour les gestionnaires sans redémarrer le serveur.
* Seuls les gestionnaires fetch et error peuvent être mis à jour.
*/
reload(options: Serve): void;
/**
* Faire une requête au serveur en cours d'exécution.
* Utile pour les tests ou le routage interne.
*/
fetch(request: Request | string): Response | Promise<Response>;
/**
* Mettre à niveau une requête HTTP vers une connexion WebSocket.
* @returns vrai si la mise à niveau réussit, faux si échec
*/
upgrade<T = undefined>(
request: Request,
options?: {
headers?: Bun.HeadersInit;
data?: T;
},
): boolean;
/**
* Publier un message à tous les clients WebSocket abonnés à un sujet.
* @returns Octets envoyés, 0 si abandonné, -1 si contre-pression appliquée
*/
publish(
topic: string,
data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer,
compress?: boolean,
): ServerWebSocketSendStatus;
/**
* Obtenir le nombre de clients WebSocket abonnés à un sujet.
*/
subscriberCount(topic: string): number;
/**
* Obtenir l'adresse IP et le port du client.
* @returns null pour les requêtes fermées ou les sockets Unix
*/
requestIP(request: Request): SocketAddress | null;
/**
* Définir un délai d'attente d'inactivité personnalisé pour une requête.
* @param seconds Délai d'attente en secondes, 0 pour désactiver
*/
timeout(request: Request, seconds: number): void;
/**
* Maintenir le processus en vie pendant l'exécution du serveur.
*/
ref(): void;
/**
* Permettre au processus de se terminer si le serveur est la seule chose en cours d'exécution.
*/
unref(): void;
/** Nombre de requêtes HTTP en cours */
readonly pendingRequests: number;
/** Nombre de connexions WebSocket actives */
readonly pendingWebSockets: number;
/** URL du serveur incluant le protocole, le nom d'hôte et le port */
readonly url: URL;
/** Port sur lequel le serveur écoute */
readonly port: number;
/** Nom d'hôte auquel le serveur est lié */
readonly hostname: string;
/** Si le serveur est en mode développement */
readonly development: boolean;
/** Identifiant de l'instance du serveur */
readonly id: string;
}
interface WebSocketHandler<T = undefined> {
/** Taille maximale du message WebSocket en octets */
maxPayloadLength?: number;
/** Octets de messages en file d'attente avant d'appliquer la contre-pression */
backpressureLimit?: number;
/** Si fermer la connexion lorsque la limite de contre-pression est atteinte */
closeOnBackpressureLimit?: boolean;
/** Appelé lorsque la contre-pression est soulagée */
drain?(ws: ServerWebSocket<T>): void | Promise<void>;
/** Secondes avant le délai d'attente d'inactivité */
idleTimeout?: number;
/** Activer la compression deflate par message */
perMessageDeflate?:
| boolean
| {
compress?: WebSocketCompressor | boolean;
decompress?: WebSocketCompressor | boolean;
};
/** Envoyer des frames ping pour maintenir la connexion en vie */
sendPings?: boolean;
/** Si le serveur reçoit ses propres messages publiés */
publishToSelf?: boolean;
/** Appelé lorsque la connexion est ouverte */
open?(ws: ServerWebSocket<T>): void | Promise<void>;
/** Appelé lorsqu'un message est reçu */
message(ws: ServerWebSocket<T>, message: string | Buffer): void | Promise<void>;
/** Appelé lorsque la connexion est fermée */
close?(ws: ServerWebSocket<T>, code: number, reason: string): void | Promise<void>;
/** Appelé lorsqu'une frame ping est reçue */
ping?(ws: ServerWebSocket<T>, data: Buffer): void | Promise<void>;
/** Appelé lorsqu'une frame pong est reçue */
pong?(ws: ServerWebSocket<T>, data: Buffer): void | Promise<void>;
}
interface TLSOptions {
/** Chaîne d'autorité de certificat */
ca?: string | Buffer | BunFile | Array<string | Buffer | BunFile>;
/** Certificat du serveur */
cert?: string | Buffer | BunFile | Array<string | Buffer | BunFile>;
/** Chemin vers le fichier de paramètres DH */
dhParamsFile?: string;
/** Clé privée */
key?: string | Buffer | BunFile | Array<string | Buffer | BunFile>;
/** Réduire l'utilisation de la mémoire TLS */
lowMemoryMode?: boolean;
/** Phrase de passe de la clé privée */
passphrase?: string;
/** Indicateurs d'options OpenSSL */
secureOptions?: number;
/** Nom du serveur pour SNI */
serverName?: string;
}