Framework backend 100 % Bun
Le backend
à son zénith.
L’ergonomie des décorateurs — contrôleurs, injection de dépendances, cycle de vie — compilée vers la table de routes native de Bun.serve. Le code expressif, l’ombre en moins.
Préversion — en attendant la publication npm, lancez le scaffolder depuis le monorepo.
import { Zenith, Controller, Get } from "zenith";
@Controller("/")
class AppController {
@Get()
hello() {
return { message: "Hello from Zenith" };
}
}
new Zenith({ controllers: [AppController] }).listen(3000);douze lignes → une table de routes native, servie par le routeur radix en Zig
- dépendance runtime dans le cœur
- 0
- dépendance runtime dans le cœur
- réflexion par requête
- 0
- réflexion par requête
- plugins officiels, du CORS à Docker
- 9
- plugins officiels, du CORS à Docker
- types de la route au navigateur
- E2E
- types de la route au navigateur
Zéro coût à l’exécution
À midi, les ombres
sont courtes.
Chaque couche exécutée à chaque requête projette une ombre sur vos performances : réflexion, recomposition des middlewares, routage JavaScript. Zenith déplace ces coûts vers la compilation — au zénith, il n’en reste presque rien.
1reflect-metadata
lecture des types par requête
2routeur JS
matching des routes en JavaScript
3recomposition middleware
chaînes rebâties par requête
4résolution DI
graphe résolu par requête
Illustration du principe — la méthodologie et les chiffres vivent dans la page Performance.
La DX d’abord
Écrivez peu.
Tout est typé, tout est testé.
Du contrôleur au client, une seule source de vérité : le schéma valide l’entrée, infère les types, alimente l’OpenAPI et le client RPC.
Des classes décorées, compilées vers la table de routes native de Bun.serve. Le routage radix tourne en Zig, pas en JavaScript. En savoir plus →
@Controller("/todos")
export class TodoController {
static readonly inject: Deps<typeof TodoController> = [TodoService];
constructor(private readonly todos: TodoService) {}
@Get()
list() {
return this.todos.list(); // objet → JSON automatique
}
@Get("/:id")
getOne(@Param("id") id: string) {
const todo = this.todos.get(id);
if (!todo) throw new NotFound(`Todo ${id} introuvable`);
return todo;
}
@Post()
create(@Body(CreateTodo) dto: CreateTodo) {
return this.todos.create(dto.title);
}
}Les fondations
Quatre piliers,
aucun compromis.
Bun-natif d’abord
Zéro dépendance runtime dans le cœur. Bun.serve, bun:sqlite, Bun.sql, Bun.redis, Bun.password, Bun.s3, WebSockets natifs — si Bun expose une primitive, Zenith l’utilise.
Approfondir →Décorateurs, coût quasi nul
Les décorateurs sont résolus à la compilation par les macros Bun : pas de reflect-metadata, pas de réflexion par requête. Le chemin chaud, c’est un appel de fonction.
Approfondir →Cœur minimal + plugins
Le cœur tient en trois mots : routing, DI, cycle de vie. Validation, CORS, auth, OpenAPI, Docker — tout le reste est un plugin que vous ajoutez explicitement.
Approfondir →Types end-to-end
Les schémas de validation infèrent l’entrée et la sortie ; le client RPC typé reflète le serveur. Une seule source de vérité, de la route au navigateur.
Approfondir →Système de plugins
Un cœur minimal.
Le reste en orbite.
Le cœur ne fait que routing, DI et cycle de vie. Les neuf plugins officiels — CORS, sécurité, OpenAPI, JWT, Docker… — gravitent autour : vous n’embarquez que ce que vous utilisez.
Bun natif
Ce que vous
n’installez plus.
Si Bun expose une primitive native, Zenith l’utilise plutôt qu’une dépendance tierce. Serveur HTTP, base de données, cache, hachage, WebSockets : le node_modules fond au soleil.
Le catalogue complet des API Bun →express / fastifyBun.servewsWebSockets natifsuuidBun.randomUUIDv7()bcrypt / argon2Bun.passwordpgBun.sqlioredisBun.redisaws-sdk (S3)Bun.s3dotenvBun.envreflect-metadatamacros à la compilation
Passez au zénith.
Un scaffolder, un contrôleur, douze lignes — et une API typée de bout en bout tourne sur Bun.