Backends TypeScript réactifs : Convex et Pylon changent la donne
Backends TypeScript réactifs : Convex et Pylon changent la donne
Depuis une dizaine d’années, l’architecture backend suit un schéma bien établi : une base de données (PostgreSQL, MySQL, MongoDB), un serveur API (Express, Fastify, NestJS), et une couche temps réel optionnelle (Socket.IO, WebSockets). Chaque couche est un système distinct, avec ses propres configurations, ses bugs, sa maintenance. Le résultat ? Beaucoup de glue code, une gestion d’état complexe, et des synchronisations qui partent en production avec un cache invalidation policy digne d’un roman de Tolkien.
Convex et Pylon proposent une approche radicalement différente : le backend réactif en TypeScript. Plus de couches séparées : le schéma, la base de données, les fonctions serveur et la synchronisation temps réel sont fusionnés en un seul système, entièrement typé et piloté depuis votre code TypeScript.
Le problème que ces frameworks résolvent
Prenons une application collaborative classique — un CRM, un éditeur de documents ou un tableau Kanban partagé. Dans une architecture traditionnelle :
// API REST classique
// 1. Définir le schéma SQL
// 2. Créer les migrations
// 3. Écrire les endpoints CRUD
// 4. Gérer l'authentification (middleware)
// 5. Ajouter des WebSockets pour le temps réel
// 6. Gérer l'invalidation de cache
// 7. Synchroniser les types entre frontend et backend
À chaque étape, des risques de désynchronisation. Le type défini dans le schéma SQL n’est pas celui que le frontend utilise. La mutation qui réussit en base ne déclenche pas la bonne mise à jour WebSocket. L’utilisateur voit des données périmées parce que le cache n’a pas été invalidé.
Convex et Pylon résolvent ce problème en partant d’un postulat simple : si le backend peut dire au frontend exactement ce qui a changé, pourquoi se compliquer la vie avec de la glue ?
Convex : la base de données réactive
Convex est un backend open-source construit autour d’une base de données réactive. Le code serveur s’écrit en TypeScript pur, directement dans le projet :
// convex/schema.ts — le schéma EST le code
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
export default defineSchema({
tasks: defineTable({
text: v.string(),
isCompleted: v.boolean(),
authorId: v.string(),
}).index("by_author", ["authorId"]),
});
Les queries sont des fonctions serveur qui lisent la base et retournent un résultat. Le système enregistre automatiquement quels documents et index chaque query consulte :
// convex/tasks.ts
import { query, mutation } from "./_generated/server";
import { v } from "convex/values";
export const list = query({
args: { authorId: v.string() },
handler: async (ctx, args) => {
return await ctx.db
.query("tasks")
.withIndex("by_author", (q) => q.eq("authorId", args.authorId))
.order("desc")
.collect();
},
});
export const add = mutation({
args: { text: v.string(), authorId: v.string() },
handler: async (ctx, args) => {
await ctx.db.insert("tasks", {
text: args.text,
isCompleted: false,
authorId: args.authorId,
});
},
});
Côté client React, l’utilisation est transparente :
import { useQuery, useMutation } from "convex/react";
import { api } from "./_generated/api";
function TaskList({ userId }) {
// Se met à jour automatiquement quand les données changent
const tasks = useQuery(api.tasks.list, { authorId: userId });
const addTask = useMutation(api.tasks.add);
if (tasks === undefined) return <Spinner />;
return (
<ul>
{tasks.map(task => (
<li key={task._id}>{task.text}</li>
))}
</ul>
);
}
Le résultat est saisissant : le useQuery se ré-exécute automatiquement à chaque mutation affectant les documents surveillés. Zéro cache manuel, zéro WebSocket à configurer. La latence est typiquement de 50 à 100 ms entre la mutation et la mise à jour du client.
Ce que Convex offre
- Typage de bout en bout — le schéma génère des types TypeScript automatiquement, utilisables côté client sans duplication
- Mutations transactionnelles — atomicité garantie, avec contrôle d’accès optimiste (OCC)
- Suivi de dépendances automatique — Convex sait exactement quelles queries impacte chaque mutation
- Recherche plein texte et vector search pour les applications IA
- Fonctions planifiées (cron) et actions longue durée
- Optimistic updates intégrées pour une UI instantanée
- Auto-hébergeable (via Docker ou binaire précompilé) ou cloud managé
Convex est développé par une startup bien financée (YC) et bénéficie d’une documentation riche et d’une large communauté. Le code source (Rust + TypeScript) est disponible sous licence FSL (conversion Apache 2.0 après 2 ans).
Pylon : le runtime binaire unique
Pylon (de PylonSync) pousse le concept encore plus loin. Là où Convex est un stack Docker multi-services, Pylon est un binaire Rust unique que vous copiez sur un VPS et lancez. Un seul processus fait tout : base de données, API, WebSocket, auth, stockage de fichiers.
// apps/api/app.ts — schéma déclaratif
import { entity, field, policy, buildManifest } from "@pylonsync/sdk";
const Note = entity("Note", {
title: field.string(),
body: field.richtext(),
authorId: field.string(),
updatedAt: field.datetime(),
}, {
indexes: [{ name: "by_author", fields: ["authorId"], unique: false }],
});
const notePolicy = policy({
name: "note_public",
entity: "Note",
allowRead: "true",
allowInsert: "auth.userId == data.authorId",
allowUpdate: "auth.userId == data.authorId",
allowDelete: "auth.userId == data.authorId",
});
const manifest = buildManifest({
name: "notes",
version: "0.1.0",
entities: [Note],
policies: [notePolicy],
});
Les fonctions serveur s’écrivent dans functions/ :
// apps/api/functions/createNote.ts
import { mutation, v } from "@pylonsync/functions";
export default mutation({
args: {
title: v.string(),
body: v.string(),
},
async handler(ctx, args) {
const id = await ctx.db.insert("Note", {
title: args.title,
body: args.body,
authorId: ctx.auth.userId,
updatedAt: new Date().toISOString(),
});
return { id };
},
});
Les reactive queries fonctionnent comme chez Convex, mais avec une approche légèrement différente :
import { db } from "@pylonsync/react";
function Feed({ userId }) {
const { data: notes, loading, error } = db.useReactiveQuery(
"getNotes",
{ userId }
);
if (loading) return <Spinner />;
return notes.map(note => <NoteCard key={note.id} note={note} />);
}
Pylon suit le même modèle que Convex — le serveur capture les dépendances lors de la première exécution et ré-exécute la query à chaque mutation affectant ces données.
Ce qui distingue Pylon
- Binaire unique —
scp pylonsur un VPS,systemctl start pylon, c’est fait - Licence MIT/Apache 2.0 — open-source sans restriction, contrairement à la licence FSL de Convex
- SQLite par défaut — un fichier, zéro configuration ; PostgreSQL disponible au besoin
- Recherche facetée native — FTS5 + roaring bitmaps, idéal pour les UI style Algolia
- Game shards — primitives tick-based pour jeux multijoueurs et applications collaboratives temps réel
- 32 plugins intégrés — TOTP, audit log, Stripe, MCP, webhooks, etc.
- CRDTs natifs — intégration Loro pour l’édition collaborative sans conflit
- Politiques ligne par ligne — les règles d’accès sont déclarées dans le schéma, pas dans un middleware
Pylon est plus récent que Convex mais bénéficie d’une architecture remarquablement propre. Son positionnement “self-host d’abord” séduit les développeurs qui veulent contrôler leur infrastructure.
Comparaison détaillée
Voici les différences clés entre les deux approches :
Architecture — Convex utilise un stack Docker avec une base de données propriétaire (Rust). Pylon est un binaire unique qui fonctionne avec SQLite ou PostgreSQL. Pour un projet personnel sur un VPS à 5$, Pylon s’installe en 30 secondes sans Docker.
Licence — Convex utilise la Functional Source License (FSL) qui interdit certains usages commerciaux sans licence payante, avec conversion Apache 2.0 après 2 ans. Pylon est MIT / Apache 2.0, libre sans restriction.
Typage — Convex a l’avantage sur la finesse du typage. L’inférence TypeScript est plus poussée, sans étape de codegen intermédiaire. Pylon nécessite une commande pylon codegen (une étape CLI) pour générer les types depuis le manifeste.
Déploiement — Convex propose un cloud managé très soigné avec un généreux free tier. L’auto-hébergement est possible mais plus complexe (Docker multi-services). Pylon est pensé pour le self-host d’abord, avec déploiement sur VPS, Vercel ou leur cloud optionnel.
Recherche — Convex a la recherche plein texte et le vector search. Pylon a la recherche plein texte (FTS5) et la recherche facetée native avec comptages en direct (live facet counts).
Cas particuliers — Pylon propose des game shards pour les jeux multijoueurs et des CRDTs pour l’édition collaborative. Convex n’a pas ces fonctionnalités nativement.
Quand choisir quoi ?
Choisissez Convex si :
- Vous voulez l’expérience TypeScript la plus soignée possible
- Le cloud managé vous convient (ou Docker ne vous fait pas peur)
- Vous avez besoin de vector search pour une application IA
- La communauté large et la documentation riche sont importantes pour vous
Choisissez Pylon si :
- Vous voulez un déploiement ultra-simple sur votre propre infrastructure
- La licence open-source sans restriction est un critère important
- Vous construisez une application avec recherche facetée (e-commerce, CRM)
- Vous avez besoin de fonctionnalités multijoueurs ou d’édition collaborative
- Vous préférez SQLite à une base de données propriétaire
Conclusion
Convex et Pylon représentent la prochaine évolution du développement backend. Le modèle réactif — où le serveur pousse automatiquement les mises à jour aux clients sans polling ni WebSockets manuels — est une avancée significative par rapport aux architectures REST traditionnelles.
Ce qui est remarquable, c’est que les deux projets sont open-source et écrits en Rust côté infra, avec une surface d’API TypeScript côté développeur. Cela leur permet d’offrir des performances élevées tout en conservant l’accessibilité de JavaScript.
La concurrence entre Convex et Pylon est saine : elle pousse chaque projet à innover. Convex investit dans le polish et l’expérience développeur ; Pylon parie sur la simplicité du déploiement et la liberté de licence. En tant que développeur, vous ne pouvez pas vous tromper — le plus dur sera de choisir.
Moi qui ai passé des années à bricoler des architectures Express + Socket.IO + Redis pub/sub, je peux vous dire que ces frameworks changent vraiment la façon de penser le backend. C’est un de ces rares moments où une nouvelle abstraction simplifie radicalement un problème complexe au lieu de l’empiler.