Nova : le web framework BEAM qui te traite comme un citoyen de première classe
Quand tu veux du web sur BEAM sans les abstractions Elixir

Le BEAM a deux web frameworks principaux. Phoenix et Nova. Phoenix est très bien mais fat et plein de feature dont tu as peut etre pas besoin pour de simples services rest. En plus si tu as du legacy en Erlang et que t’a pas envie de te taper la migration, Nova résout ce problème. Et il le fait bien.
Lexique
C’est là mes définitions et si elles vous déplaisent, cassez-vous je suis pas là pour discuter lexique.
- Nova: web framework pour le BEAM, supporte Erlang, Elixir et LFE. MVC, plugin pipeline, WebSockets, PubSub. Construit sur Cowboy.
- BEAM: la machine virtuelle d’Erlang. Le truc qui fait tourner Erlang, Elixir, LFE, et Nova.
- Cowboy: serveur HTTP Erlang, léger et rapide. Nova l’utilise comme backend HTTP.
- OTP: Open Telecom Platform — le framework de patterns pour Erlang. Supervision trees, GenServer, releases. Nova est OTP-native.
- Plugin pipeline: middleware composable avec pre/post request hooks. CORS, CSRF, auth — tout est un plugin.
- Kura: couche de base de données pour Erlang, inspirée par Ecto. Schemas, changesets, migrations, queries.
- nova_resilience: module de résilience pour Nova. Circuit breakers, bulkheads, health checks, graceful shutdown.
- ErlyDTL: moteur de templates pour Erlang, basé sur Django Template Language.
- Arizona: module de live views pour Nova, équivalent de LiveView pour Phoenix.
- Hikyaku: module d’email transactionnel pour Nova.
Pourquoi Nova ?
Le problème c’est simple. Si tu as une codebase Erlang existante et que tu veux ajouter une API REST, tu fais quoi ? Et si tu as veux une codebase plus simple et pas toute la lourdeur de Phoenix?
Tu finis avec Cowboy brut. Ou tu essayes de faire tourner Phoenix dans un mixte Erlang/Elixir. C’est possible mais un poil foireux.
Avec Nova pas besoin de mixte Erlang/Elixir. Le framework est plus lean, ainsi ta codebase sera aussi bien plus concise et simples à comprendre.
Architecture
Haute vue
Client (HTTP/WebSocket)
│
└── Cowboy (serveur HTTP)
│
├── Nova Router
│ ├── Path parameters, prefixes
│ ├── Per-route security
│ └── Environment-based routing
│
├── Plugin Pipeline (pre_request)
│ ├── CORS
│ ├── CSRF
│ ├── Correlation ID
│ └── Custom plugins
│
├── Security Middleware
│ └── Authentication/Authorization
│
├── Controller
│ ├── {json, Map}
│ ├── {ok, Variables}
│ ├── {status, Code}
│ ├── {redirect, Path}
│ └── {sendfile, ...}
│
├── Plugin Pipeline (post_request)
│
└── Response
Request pipeline
Request → Cowboy → Nova Router → Plugins (pre) → Security → Controller → Plugins (post) → Response
C’est du MVC classique, adapté au Erlang. Rien de révolutionnaire ici.
Ecosystem
Nova Framework
├── nova (core framework)
├── rebar3_nova (scaffolding & generators)
├── nova_test (testing utilities)
├── kura (database layer — Ecto-inspired)
├── rebar3_kura (migration generator)
├── opentelemetry_nova (tracing & metrics)
├── nova_resilience (circuit breakers, bulkheads, health)
├── nova_websocket (WebSocket behaviour)
├── nova_pubsub (distributed pub/sub)
├── Arizona (live views)
└── Hikyaku (email transactionnel)
Features Clés
Routing
Path parameters, prefixes, per-route security, environment-based routing. C’est du MVC classique, adapté au Erlang.
Controllers
Les controllers sont des fonctions Erlang qui prennent un request map et retournent des tuples. {json, Map}, {ok, Variables}, {status, Code}, {redirect, Path}, {sendfile, ...}.
C’est simple. C’est direct. C’est Erlang.
Plugin Pipeline
Des hooks composable pour auth, CORS, logging, et custom concerns. Configurable par route group. Plus flexible que Plug.
WebSockets et PubSub
WebSockets natifs via nova_websocket. PubSub distribué via OTP’s pg module. Marche sur des nodes clusterés. Pas besoin de Redis.
Putain, c’est beau.
Templates (ErlyDTL)
ErlyDTL — Django Template Language pour Erlang. Hot reload via rebar3 nova serve.
Kura (Database Layer)
0%% Schema
1-module(pets).
2-include_lib("kura/include/schema.hrl").
3
4-export([schema/0]).
5
6schema() ->
7 #schema{
8 name = pets,
9 fields = [
10 #field{name = name, type = string},
11 #field{name = species, type = string},
12 #field{name = owner_id, type = integer, references = users}
13 ]
14 }.
15
16%% Changeset
17changeset(Pet, Params) ->
18 kura:cast(Pet, Params, [name, species, owner_id])
19 |> kura:validate_required([name, species])
20 |> kura:validate_length(:name, min: 1, max: 100).
Kura est l’inspiration Ecto pour Erlang. Schemas, changesets, migrations, queries, associations. Si tu connais Ecto, Kura te familier.
nova_resilience — La feature qui tue
C’est LA feature qui distingue Nova de Phoenix. nova_resilience fournit :
- Health endpoints —
/health,/ready,/livepour Kubernetes probes - Startup gating — traffic held jusqu’à ce que les dépendances critiques soient healthy
- Circuit breakers — arrêter d’appeler les dépendances qui échouent
- Bulkheads — limiter les requêtes concurrentes par dépendance
- Retry — retry configurable avec exponential backoff et jitter
- Deadline propagation — timeouts par request via headers
- Graceful shutdown — teardown ordonné avec drain et coordination LB
0{nova_resilience, [
1 {dependencies, [
2 #{name => primary_db,
3 type => database,
4 adapter => pgo,
5 critical => true,
6 shutdown_priority => 1,
7 breaker => #{
8 failure_threshold => 5,
9 wait_duration => 30000
10 },
11 bulkhead => #{
12 max_concurrent => 100
13 }
14 },
15 #{name => kafka,
16 type => kafka,
17 adapter => brod,
18 critical => false,
19 shutdown_priority => 10
20 }
21 ]},
22 {health_check_interval, 10000},
23 {vm_checks, true},
24 {gate_enabled, true},
25 {gate_timeout, 30000},
26 {shutdown_delay, 5000},
27 {shutdown_drain_timeout, 15000}
28]}.
Et c’est tout. /health, /ready, /live sont exposés. Circuit breakers, bulkheads, graceful shutdown — tout est configuré. Pas de code manuel.
Tu vois le problème avec Phoenix ? Il n’a rien de comparable intégré. Tu dois configurer 5 libraries externes pour avoir la même chose.
Sub-applications
Comme des Rails engines. Tu peux monter des Nova apps sous des path prefixes. Utile pour les monolithes modulaires.
Hot Code Upgrades
Nova est OTP-native. Hot code upgrades, rolling upgrades, releases: tout est supporté nativement. Pas besoin d’outils externes.
Nova vs Phoenix — Le comparatif
| Critère | Nova | Phoenix |
|---|---|---|
| Langage | Erlang (et Elixir, LFE) | Elixir uniquement |
| HTTP Server | Cowboy | Cowboy ou Bandit |
| OTP-native | Oui — supervision trees, releases | Oui — mais plus d’abstraction |
| Plugin pipeline | Oui — pre/post hooks | Non — Plug middleware |
| WebSockets | nova_websocket behaviour |
Phoenix Channels |
| PubSub | OTP’s pg distribué |
Phoenix PubSub |
| Live views | Arizona | Phoenix LiveView |
| Database | Kura (Ecto-inspired) | Ecto (plus mature) |
| Résilience | nova_resilience (circuit breakers, bulkheads) |
Pas intégré (utiliser des libs externes) |
| Health checks | /health, /ready, /live natifs |
Pas intégré |
| Graceful shutdown | Ordre configurable avec drain | Pas intégré |
| Templates | ErlyDTL (Django-style) | HEEx (HTML + Embedded Elixir) |
| Community | ~300 stars, petite | ~22k stars, massive |
| Documentation | Nova Book, guides | Extensive, beaucoup de tutos |
| Production use | Limité | Massif (Discord, etc.) |
| Multi-langage | Erlang + Elixir + LFE | Elixir uniquement |
| License | Apache 2.0 | Apache 2.0 |
Le choix Nova vs Phoenix, c’est un choix de langage et de niveau d’abstraction.
- Si tu écris du Erlang et que tu veux moins d’abstraction → Nova
- Si tu écris du Elixir et que tu veux le meilleur écosystème → Phoenix
- Si tu veux de la résilience intégrée (circuit breakers, health checks) → Nova
- Si tu veux du LiveView mature → Phoenix
- Si tu veux du multi-langage BEAM → Nova
Les deux sont d’excellent frameworks. Tout est trade-off.
Cas d’Usage
API REST pour une codebase Erlang existante
Si tu as déjà du Erlang en prod et que tu veux ajouter une API REST, Nova est le choix naturel. Pas besoin de migrer vers Elixir. Pas besoin de mixte Erlang/Elixir. juste rebar3 new nova my_api.
Microservice avec résilience intégrée
Si tu veux des circuit breakers, des health checks pour Kubernetes, du graceful shutdown — nova_resilience fait tout. Pas besoin de 5 libraries externes.
Monolithe modulaire
Avec les sub-applications, tu peux monter des Nova apps sous des path prefixes. /admin = une app, /api = une autre. Architecture modulaire sans microservices.
App distribuée avec PubSub
PubSub distribué via OTP’s pg. Pas de Redis. Pas de Kafka. Juste du BEAM natif. Les messages passent entre les nodes du cluster automatiquement.
Hot code upgrades
Si tu as besoin de déployer sans downtime, Nova est OTP-native. Hot code upgrades, rolling upgrades — tout est supporté nativement.
Risques et Mitigations
| Risque | Impact | Mitigation |
|---|---|---|
| Community petite | Modéré | Nova est jeune mais croît. Cowboy est battle-tested. |
| Documentation limitée | Modéré | Le Nova Book existe. Les guides sont là. |
| Kura moins mature qu’Ecto | Faible-Modéré | Kura couvre les use cases courants. Pour les edge cases, utiliser Erlang directement. |
| Production use limité | Modéré | Nova tourne en prod. Pas autant que Phoenix, mais ça marche. |
| ErlyDTL old-school | Faible | ErlyDTL marche. Arizona est plus moderne pour les live views. |
| Pas de LiveView mature | Modéré | Arizona existe. Pour du live view complexe, Phoenix est mieux. |
| Multi-langage = complexité | Faible | C’est un feature, pas un bug. Mais faut gérer les 3 langages. |
Prérequis
- Erlang/OTP 23+ (28+ pour Arizona)
- Rebar3 — build tool Erlang
- Docker — pour PostgreSQL (optionnel)
- OS: Linux, macOS, Windows
- Taille: ~10 MB (core framework)
- Memory: 20 MB par container (si utilisé avec Incus)
Mot de la fin
Nova utilise toute la puissance de Erlang/BEAM avec moins d’abstranction et de “boilerplate” que Phoenix. Il brille dans de petit services simples et ultra-résilients.
Et si tu écris du Erlang, c’est le seul web framework mature qui te traite comme un citoyen de première classe.
See ya space-cowboy!