Retour au blog

Bun 1.2 : Le Runtime JavaScript Qui Bat des Records de Performance

Salut HaWkers, si vous suivez l'écosystème JavaScript, vous avez probablement entendu parler de Bun. Ce runtime relativement nouveau conquiert les développeurs avec des chiffres de performance impressionnants.

Vous vous êtes déjà frustré en attendant que npm install se termine ? Ou peut-être avez-vous senti que votre serveur Node.js pourrait être plus rapide ? Bun promet de résoudre ces problèmes et bien plus encore.

Qu'est-ce que Bun

Bun est un runtime JavaScript et toolkit tout-en-un qui inclut :

Composants Principaux :

  • Runtime JavaScript (alternative à Node.js)
  • Package manager (alternative à npm/yarn/pnpm)
  • Bundler (alternative à webpack/esbuild)
  • Test runner (alternative à Jest/Vitest)
  • Transpileur TypeScript natif

Le différenciateur ? Tout cela a été écrit en Zig et C++ pour une performance maximale, utilisant JavaScriptCore (moteur de Safari) au lieu de V8.

Nouveautés de Bun 1.2

Windows Natif

Enfin, un support complet pour Windows :

# Installation sur Windows (PowerShell)
powershell -c "irm bun.sh/install.ps1 | iex"

# Ou via npm
npm install -g bun

# Vérifier l'installation
bun --version
# 1.2.0

Avant la version 1.2, Bun sur Windows dépendait de WSL. Maintenant il fonctionne nativement.

S3 Object Storage Natif

Bun 1.2 a introduit des APIs natives pour Amazon S3 :

// Accès natif à S3 - sans SDKs lourds
import { S3Client } from "bun";

const s3 = new S3Client({
  accessKeyId: Bun.env.AWS_ACCESS_KEY_ID,
  secretAccessKey: Bun.env.AWS_SECRET_ACCESS_KEY,
  region: "us-east-1"
});

// Upload de fichier
const file = Bun.file("./image.png");
await s3.write("my-bucket/image.png", file);

// Download
const downloaded = await s3.file("my-bucket/image.png");
await Bun.write("./downloaded.png", downloaded);

// Streaming de gros fichiers
const stream = s3.file("my-bucket/large-video.mp4").stream();

Postgres Natif

Driver PostgreSQL intégré au runtime :

// Sans installer pg ou postgres.js
import { SQL } from "bun";

const sql = new SQL({
  hostname: "localhost",
  database: "myapp",
  username: "user",
  password: "password"
});

// Queries avec tagged templates
const users = await sql`SELECT * FROM users WHERE active = true`;

// Queries paramétrées (sécurisées contre SQL injection)
const userId = 123;
const user = await sql`SELECT * FROM users WHERE id = ${userId}`;

// Transactions
await sql.begin(async (tx) => {
  await tx`UPDATE accounts SET balance = balance - 100 WHERE id = 1`;
  await tx`UPDATE accounts SET balance = balance + 100 WHERE id = 2`;
});

Performance Comparée

Package Manager

Comparons l'installation de dépendances :

# Projet avec ~50 dépendances

# npm
time npm install
# real: 45.2s

# yarn
time yarn install
# real: 32.1s

# pnpm
time pnpm install
# real: 18.7s

# bun
time bun install
# real: 2.3s

Bun est systématiquement 10-20x plus rapide que npm.

Performance Runtime

Serveur HTTP :

// Bun native server
Bun.serve({
  port: 3000,
  fetch(req) {
    return new Response("Hello World");
  }
});

// Résultats : ~185,000 req/s

Comparatif de requêtes par seconde :

Runtime Hello World JSON API File Serve
Bun 1.2 185,000 142,000 78,000
Deno 2.0 125,000 95,000 45,000
Node 22 118,000 88,000 52,000

Bundler

# Build d'un gros projet React (~500 composants)

# webpack
time npx webpack build
# real: 28.4s

# esbuild
time npx esbuild src/index.tsx --bundle --outdir=dist
# real: 0.8s

# bun
time bun build src/index.tsx --outdir=dist
# real: 0.4s

Utiliser Bun en Pratique

Créer un Projet

# Créer un projet
mkdir mon-projet && cd mon-projet
bun init

# Structure créée :
# mon-projet/
#   .gitignore
#   bun.lockb
#   index.ts
#   package.json
#   README.md
#   tsconfig.json

Serveur Web Complet

// server.ts
import { Hono } from "hono";
import { cors } from "hono/cors";
import { logger } from "hono/logger";

const app = new Hono();

// Middlewares
app.use("*", logger());
app.use("/api/*", cors());

// Connexion database (native Bun)
const db = new SQL({
  hostname: Bun.env.DB_HOST,
  database: Bun.env.DB_NAME,
  username: Bun.env.DB_USER,
  password: Bun.env.DB_PASSWORD
});

// Routes
app.get("/", (c) => c.text("Hello Bun!"));

app.get("/api/users", async (c) => {
  const users = await db`SELECT id, name, email FROM users`;
  return c.json(users);
});

app.post("/api/users", async (c) => {
  const body = await c.req.json();
  const { name, email } = body;

  const [user] = await db`
    INSERT INTO users (name, email)
    VALUES (${name}, ${email})
    RETURNING *
  `;

  return c.json(user, 201);
});

// File uploads (avec S3 natif)
app.post("/api/upload", async (c) => {
  const form = await c.req.formData();
  const file = form.get("file") as File;

  const s3 = new S3Client({
    accessKeyId: Bun.env.AWS_ACCESS_KEY_ID!,
    secretAccessKey: Bun.env.AWS_SECRET_ACCESS_KEY!,
    region: "us-east-1"
  });

  const key = `uploads/${Date.now()}-${file.name}`;
  await s3.write(`my-bucket/${key}`, file);

  return c.json({ url: `https://my-bucket.s3.amazonaws.com/${key}` });
});

export default {
  port: 3000,
  fetch: app.fetch
};

Exécuter et Développer

# Exécuter en développement (avec hot reload)
bun --watch run server.ts

# Exécuter en production
bun run server.ts

# Scripts du package.json
bun run dev
bun run build
bun run test

Système de Tests

Bun a un test runner intégré compatible avec Jest :

// math.ts
export function add(a: number, b: number): number {
  return a + b;
}

export function multiply(a: number, b: number): number {
  return a * b;
}

export async function fetchUser(id: number) {
  const response = await fetch(`https://api.example.com/users/${id}`);
  return response.json();
}
// math.test.ts
import { describe, test, expect, mock, beforeEach } from "bun:test";
import { add, multiply, fetchUser } from "./math";

describe("Math functions", () => {
  test("add should sum two numbers", () => {
    expect(add(2, 3)).toBe(5);
    expect(add(-1, 1)).toBe(0);
  });

  test("multiply should multiply two numbers", () => {
    expect(multiply(3, 4)).toBe(12);
    expect(multiply(0, 100)).toBe(0);
  });
});

describe("API functions", () => {
  beforeEach(() => {
    // Mock du fetch global
    global.fetch = mock(async (url: string) => {
      return new Response(JSON.stringify({
        id: 1,
        name: "Test User"
      }));
    });
  });

  test("fetchUser should return user data", async () => {
    const user = await fetchUser(1);
    expect(user.name).toBe("Test User");
  });
});
# Exécuter les tests
bun test

# Avec coverage
bun test --coverage

# Watch mode
bun test --watch

# Filtrer les tests
bun test --filter "Math"

Bundler Intégré

// bun.config.ts (optionnel)
export default {
  entrypoints: ["./src/index.tsx"],
  outdir: "./dist",
  target: "browser",
  minify: true,
  sourcemap: "external",
  splitting: true,
  plugins: [
    // Plugins personnalisés
  ]
};
# Build simple
bun build ./src/index.tsx --outdir ./dist

# Build avec minification
bun build ./src/index.tsx --outdir ./dist --minify

# Build pour Node.js
bun build ./src/server.ts --outdir ./dist --target node

# Watch mode
bun build ./src/index.tsx --outdir ./dist --watch

Macros en Temps de Build

Une feature unique de Bun sont les macros qui s'exécutent au moment de la compilation :

// config.ts
export const VERSION = await Bun.file("./version.txt").text();
export const BUILD_TIME = new Date().toISOString();

// Ceci est évalué au moment du build, pas au runtime !
export const ENV_CONFIG = {
  apiUrl: Bun.env.API_URL,
  debug: Bun.env.DEBUG === "true"
};
// app.ts
import { VERSION, BUILD_TIME } from "./config" with { type: "macro" };

console.log(`Version: ${VERSION}`);
console.log(`Built at: ${BUILD_TIME}`);

// Dans le bundle final, ces valeurs sont substituées par des strings littérales

Compatibilité avec Node.js

Bun est hautement compatible avec Node.js :

// La plupart du code Node.js fonctionne sans changements
import express from "express";
import { PrismaClient } from "@prisma/client";
import bcrypt from "bcrypt";

const app = express();
const prisma = new PrismaClient();

app.use(express.json());

app.post("/login", async (req, res) => {
  const { email, password } = req.body;

  const user = await prisma.user.findUnique({
    where: { email }
  });

  if (!user) {
    return res.status(401).json({ error: "User not found" });
  }

  const valid = await bcrypt.compare(password, user.password);

  if (!valid) {
    return res.status(401).json({ error: "Invalid password" });
  }

  res.json({ user: { id: user.id, email: user.email } });
});

// Fonctionne avec bun run ou node
app.listen(3000);

APIs Node.js Supportées

// La plupart des APIs Node.js fonctionnent
import fs from "fs";
import path from "path";
import crypto from "crypto";
import { Buffer } from "buffer";
import { EventEmitter } from "events";
import { Readable, Writable } from "stream";

// process global
console.log(process.env.NODE_ENV);
console.log(process.cwd());

// APIs Bun additionnelles
const file = Bun.file("./data.json");
const content = await file.text();

const hash = Bun.hash("sha256", "data");

Quand Utiliser Bun

Cas Idéaux

1. Nouveaux Projets Nécessitant de la Performance :

// APIs haute performance
// Traitement de données en temps réel
// Serveurs WebSocket
// Microservices

Bun.serve({
  port: 3000,
  websocket: {
    message(ws, message) {
      // Broadcast à tous les clients
      server.publish("chat", message);
    }
  },
  fetch(req, server) {
    if (server.upgrade(req)) {
      return; // Upgrade vers WebSocket
    }
    return new Response("Hello!");
  }
});

2. Scripts et Outils CLI :

#!/usr/bin/env bun

// Script de migration de données
import { SQL } from "bun";

const sourceDb = new SQL({ /* config source */ });
const targetDb = new SQL({ /* config destination */ });

console.log("Starting migration...");

const users = await sourceDb`SELECT * FROM users`;

for (const user of users) {
  await targetDb`
    INSERT INTO users (id, name, email)
    VALUES (${user.id}, ${user.name}, ${user.email})
  `;
  console.log(`Migrated user ${user.id}`);
}

console.log(`Migration complete! ${users.length} users migrated.`);

3. Quand le Temps de Build Compte :

# CI/CD plus rapide
# bun install + bun build en secondes
# vs npm install + webpack en minutes

Quand Éviter (Pour l'Instant)

  • Projets dépendant de native addons complexes
  • Écosystèmes très spécifiques à Node.js
  • Quand la stabilité est plus importante que la performance
  • Équipes qui ne peuvent pas absorber la courbe d'apprentissage

Migrer de Node.js

Étape 1 : Installer Bun

# Unix/Mac
curl -fsSL https://bun.sh/install | bash

# Windows
powershell -c "irm bun.sh/install.ps1 | iex"

Étape 2 : Convertir package.json

# Supprimer node_modules et lockfiles anciens
rm -rf node_modules package-lock.json yarn.lock pnpm-lock.yaml

# Installer avec Bun
bun install
# Crée bun.lockb

Étape 3 : Tester la Compatibilité

# Exécuter les tests
bun test

# Exécuter l'application
bun run dev

# Vérifier que tout fonctionne

Étape 4 : Optimiser pour Bun

// Remplacer les packages par des APIs natives quand possible

// Avant : pg ou postgres.js
import { Client } from "pg";

// Après : SQL natif de Bun
import { SQL } from "bun";

// Avant : aws-sdk pour S3
import AWS from "aws-sdk";

// Après : S3 natif de Bun
import { S3Client } from "bun";

Conclusion

Bun 1.2 représente une avancée significative dans l'écosystème JavaScript. Avec une performance impressionnante, des APIs natives pour les tâches courantes, et une excellente compatibilité avec Node.js, il devient une option de plus en plus viable pour les projets sérieux.

La principale recommandation est : expérimentez. Commencez par un nouveau projet ou un script, et étendez à mesure que vous gagnez en confiance. L'écosystème Bun mûrit encore, mais la direction est prometteuse.

Si vous voulez comparer Bun avec d'autres options de runtime, je recommande de jeter un œil à un autre article : Deno 2.0 vs Node.js : La Bataille des Runtimes où vous découvrirez comment chaque runtime se positionne sur le marché actuel.

C'est parti ! 🦅

Commentaires (0)

Cet article n'a pas encore de commentaires. Soyez le premier!

Ajouter des commentaires