Reciprok Docs

ADR-04, API layer (Eden Treaty)

Eden Treaty + Elysia plutôt que tRPC. Pourquoi.

Statut : Accepté Date : 2026-04 Sujet : Choix de la couche API typée bout-en-bout pour Reciprok

Contexte

Reciprok a besoin d'une couche API qui :

  1. Type inference end-to-end entre serveur et client (frontend Next.js + tools IA)
  2. Intégration native avec Bun (notre runtime cible)
  3. Streaming pour le chat IA
  4. Lifecycle hooks pour brancher : auth, audit log (timeline events), métriques, logs
  5. Validation des inputs/outputs avec un schéma partagé
  6. Pas de codegen ou d'étape de build supplémentaire

Alternatives considérées

Option 1, tRPC + Express/Fastify

L'option historique de l'écosystème Better-T-Stack.

Pour :

  • Écosystème mature, beaucoup d'exemples
  • Type inference excellente
  • Intégration React Query out of the box

Contre :

  • Pas de support Bun first-class, tourne mais pas optimisé
  • Couche d'abstraction supplémentaire (procedures vs routes HTTP)
  • Lifecycle moins flexible que les hooks Elysia natifs
  • Streaming SSE moins propre (subscriptions = WebSocket dans la doc tRPC)
  • L'intégration avec OpenAPI nécessite un plugin tiers (trpc-openapi)
  • Pas de macros : on duplique souvent les middleware pour les guards

Option 2, REST manuel + Zod + génération de types

Définir des routes REST classiques, valider avec Zod, et générer un client typé via OpenAPI.

Pour :

  • Standard, lisible par n'importe quel dev
  • OpenAPI gratuit

Contre :

  • Codegen = étape de build supplémentaire
  • Type inference moins fluide qu'Eden ou tRPC
  • Beaucoup de boilerplate

Option 3, Elysia + Eden Treaty (retenu)

Elysia est un framework HTTP Bun-first, performant (~21x plus rapide qu'Express selon les benchmarks officiels), avec un système de type inference qui se propage à un client typé via le package @elysiajs/eden.

Pour :

  • Bun-native : Reciprok tourne sur Bun, autant utiliser un framework conçu pour
  • Type inference end-to-end sans codegen : treaty<App>(...) infère les types directement depuis le typeof app exporté côté serveur. Aucun build, aucun fichier généré.
  • Lifecycle hooks first-class : onRequest, parse, transform, beforeHandle, afterHandle, mapResponse, onAfterResponse, onError. Idéal pour brancher l'audit log, les métriques, GlitchTip.
  • Macros : système de "guards" réutilisables (auth, permissions, rate limit) qui s'attachent aux routes et propagent leur typage au client. Beaucoup plus propre que les middlewares tRPC.
  • Modèle TypeBox : validation native, pas de wrapper Zod externe (mais Zod reste utilisable côté domaine si besoin)
  • Plugin deduplication : si un module est mounté plusieurs fois (tests), il n'est exécuté qu'une fois
  • Streaming SSE natif : async function* qui yield les chunks, supporté nativement
  • OpenAPI auto-généré via @elysiajs/swagger (un plugin officiel, pas un fork tiers)
  • Performance imbattable sur Bun

Contre :

  • Plus jeune que tRPC, écosystème React Query un peu moins fourni
  • Documentation parfois en avance sur les exemples publics
  • Moins connu de la communauté → onboarding plus long pour un nouvel arrivant

Décision

Elysia côté serveur + Eden Treaty côté client.

// Server
export const app = new Elysia()
  .use(appContext)
  .use(authMacro)
  .group('/api/v1', (api) => api.use(membersRoutes).use(requestsRoutes));

export type App = typeof app;

// Client
import { treaty } from '@elysiajs/eden';
import type { App } from '@reciprok/server';
export const api = treaty<App>(process.env.NEXT_PUBLIC_API_URL!);

Le détail des best practices (method chaining, derive/decorate, macros, hooks, models) est dans la page Design d'API.

Conséquences

Positives :

  • Pas de codegen, pas d'étape de build pour le client API
  • Type inference end-to-end y compris pour les valeurs injectées par les macros (user, member, traceId)
  • Hooks lifecycle natifs idéaux pour brancher la timeline et le monitoring
  • Performance Bun maximale
  • OpenAPI auto-généré sert aussi de doc pour les tools IA

Négatives :

  • Onboarding nouveau dev : il faut lire la doc Elysia (ce qui est fait, le framework est bien documenté)
  • Eden ne wrap pas encore parfaitement les streams SSE → on consomme avec fetch natif côté client pour le chat IA
  • Si on veut passer à un autre runtime (Node, Deno), Elysia tourne mais perd ses optimisations Bun

Migration depuis tRPC ?

Pas applicable : Reciprok est greenfield, on commence directement en Eden Treaty.

Voir aussi

On this page