Bun est un nouveau runtime JavaScript, comme Node ou Deno, il a commencé à faire parler de lui en Juillet de cette année, notamment via un tweet de son créateur.
23:19 PM - 05 Jul 2022
Bun est construit autour de 3 principaux piliers:
- Démarrage rapide
- Gros niveau de performance
- Outil complet (package manager, bundler, transpiler, et beaucoup d'api disponibles)
Pour tenir ces promesses, Bun utilise le moteur JavaScriptCore (qui d'après la page du site démarre et fonctionne plus vite que V8).
Bun est en grande partie écrit en ZIG notamment le transpiler JSX/TypeScript, le client npm ou encore le client http ... .
La grande majorité des API disponibles a été réécrite de zéro.
Dans cet article, j'aimerais utiliser Bun pour écrire une petite API permettant de gérer des inscriptions à une newsletter, rien de fou niveau métier, il s'agit juste d'un prétexte pour écrire une petite application et utiliser l'API SQLite nativement supportée par Bun.
Installation
Bun fournit un script pour l'installation, pour le moment il est possible de l'installer sur macOs x64 et Apple Sillicon, Linux x64, et le Windows Subsystem for Linux.
Pour ma part, étant repassé sur PC depuis peu, je vais utiliser le WSL avec Ubuntu 20.04.
Pour exécuter le script d'installation:
curl https://bun.sh/install | bash
Pour vérifier que l'installation fonctionne et avoir une première idée des commandes disponibles:
bun help
Vous devriez avoir cet affichage:
Je ne vais pas rentrer dans le détail de toutes les commandes dans cet article, mais davantage me concentrer sur celles utiles pour construire mon exemple. La documentation est disponible ici.
Premier script
Pour valider le fonctionnement de mon installation, j'ai utilisé le script d'exemple présent sur la homepage du site.
export default {
port: 3000,
fetch(request) {
return new Response("Welcome to Bun!");
},
};
le code est disponible dans le fichier premier-script.js
Pour lancer le script:
bun run premier-script.js
Une visite sur http://localhost:3000 doit vous répondre "Welcome to Bun!".
Parfait, ça fonctionne !
Bun supporte aussi TypeScript, pour ajouter les types à mon projet, je vais utiliser la commande suivante:
bun add -d bun-types
et ensuite ajouter la propriété type
dans le fichier tsconfig.json:
{
"compilerOptions": {
"types": ["bun-types"]
}
}
on peut donc réécrire notre premier script en TypeScript, ce qui donne quelque chose comme :
Bun.serve({
fetch(req: Request) {
return new Response("Welcome to Bun with TS!");
},
port: 3000,
});
le code est disponible dans le fichier premier-script-ts.ts
Un truc intéressant, c'est le support natif des fichiers .env
par la commande bun run
, ce qui nous permet de configurer facilement nos applis à l'exécution. Les variables d'environnement sont disponibles dans process.env.XXXX.
La lecture des fichiers est faite selon l'ordre suivant:
.env.local
- if ($NODE_ENV === "production")
.env.production
else.env.development
.env
Un exemple de l'utilisation des variables d'environnement est disponible dans le fichier using-env.ts
Développer une API simple avec Bun
Comme je l'ai dit dans l'intro, mon objectif est d'écrire une API très simple pour gérer les inscriptions à une newsletter, un POST
avec un email, une petite vérification si l'email est déjà présent en base (pour simuler une logique métier) et la sauvegarde dans une table SQLite. Une seconde route permettra de récupérer les inscriptions.
En Node, j'ai l'habitude d'utiliser Express pour développer mes API, malheureusement ce n'est pas (encore ?) possible avec Bun (certaines API utilisées par Express sont propres à V8 et nécessitent le développement d'un polyfill).
Lorsque j'ai commencé à suivre le projet, courant juillet, je suis un peu resté sur ma faim à ce niveau, impossible pour moi de continuer mes tests, et il était difficile d'envisager Bun pour autre chose que des applications React.
En creusant un peu le sujet, cette semaine, j'ai découvert le framework Hono qui est supporté par Bun ainsi que par d'autres runtimes comme les Workers Cloudflare et deno entre autres.
Pour ajouter hono dans le projet:
bun add hono
Et une première api simple:
import { Hono } from "hono";
const app = new Hono();
app.get("/", (c) => c.text("Hello! Hono!"));
Bun.serve({
port: parseInt(process.env.PORT),
fetch: app.fetch,
});
le code est disponible dans le fichier hono.ts
Pour la base de données, Bun supporte nativement SQLite, via le module bun:sqlite
, ce module n'a pas besoin d'être installé il est directement intégré dans Bun.
La documentation du module est disponible ici.
En l'état le module semble assez complet, mais pour cet exemple je ne vais pas avoir besoin de grand chose: j'ai besoin de récupérer un ensemble d'enregistrements, un enregistrement unitaire et d'en insérer un nouveau. J'ai aussi besoin de créer la table.
Bun propose de créer un objet de type Database
qui nous servira pour les différentes opérations.
import { Database } from "bun:sqlite";
const db = new Database("mydb.sqlite");
Pour créer la table et insérer un enregistrement, nous utilisons la méthode run
.
db.run(
"CREATE TABLE IF NOT EXISTS foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT)"
);
db.run("INSERT INTO foo (greeting) VALUES (?)", "Welcome to bun!");
db.run("INSERT INTO foo (greeting) VALUES (?)", "Hello World!");
Pour les requêtes la méthode query
est suivie de all
ou get
// get the first row
db.query("SELECT * FROM foo").get();
// { id: 1, greeting: "Welcome to bun!" }
// get all rows
db.query("SELECT * FROM foo").all();
// [
// { id: 1, greeting: "Welcome to bun!" },
// { id: 2, greeting: "Hello World!" },
// ]
// get all rows matching a condition
db.query("SELECT * FROM foo WHERE greeting = ?").all("Welcome to bun!");
// [
// { id: 1, greeting: "Welcome to bun!" },
// ]
// get first row matching a named condition
db.query("SELECT * FROM foo WHERE greeting = $greeting").get({
$greeting: "Welcome to bun!",
});
// [
// { id: 1, greeting: "Welcome to bun!" },
// ]
Dans la doc de Bun, il y a un benchmark réalisé à partir de la base Northwind Traders (souvenir de Access :) ), qui semble donner des résultats prometteurs.
Voici donc le code correspondant à la petite API de gestion des inscriptions, je passe volontairement sur les détails concernant l'utilisation de Hono.
import { Hono } from "hono";
import { logger } from "hono/logger";
import { Database } from "bun:sqlite";
interface Subscription {
id: number;
email: string;
}
const db = new Database();
// Pour stocker la base dans un fichier
// const db = new Database("newsletter.sqlite");
db.run(
"CREATE TABLE IF NOT EXISTS subscription (id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT)"
);
const app = new Hono();
app.use("*", logger());
app.get("/", (c) => {
const subs = db.query("SELECT * FROM subscription").all();
return c.json(subs);
});
app.post("/", async (c) => {
const body = await c.req.json<Subscription>();
const sub = db
.query("SELECT * From subscription WHERE email = ?")
.get<Subscription>(body.email);
if (!sub) {
db.run("INSERT INTO subscription (email) VALUES (?)", body.email);
return c.json(body, 201);
}
return c.json(body, 409);
});
Bun.serve({
port: parseInt(process.env.PORT),
fetch: app.fetch,
});
le code est disponible dans le fichier newsletter.ts
Pour conclure
Je trouve le projet vraiment intéressant, Jarred Sumner a fait un boulot titanesque pour en arriver là. Au delà de l'aspect performant des choses, j'aime particulièrement le tout en un, d'autant plus que je pense utiliser Bun pour prototyper rapidement des idées.
Apres ma petite déception de début juillet, je suis content d'avoir découvert Hono qui me permet d'envisager véritablement de continuer avec Bun.
Bref, je vais expérimenter sur quelques idées et je garde un œil sur le projet, d'autant plus qu'il y a maintenant une structure pour le soutenir, Oven.
Les sources des exemples sont disponibles ici.
Crédits: Photo by Gil Ndjouwou on Unsplash
Top comments (0)