Boilerplate SaaS multi-tenant avec Next.js, Prisma et tRPC
Multi-tenant,
pas juste promis. Vérifié par tests.
La plupart des boilerplates ajoutent une colonne organizationId et vous laissent le filtre à vous. HeartCo automatise les lectures, cadre les écritures par une règle simple, et fournit les tests qui le prouvent.
Comment l'isolation fonctionne
Une extension Prisma pour les lectures, une règle simple pour les écritures.
Lectures auto-filtrées
ctx.orgDb ajoute organizationId à findMany, findFirst, findFirstOrThrow, count, aggregate et groupBy. Rien à écrire, rien à oublier.
Écritures explicites, par design
create, update et delete ne sont pas couverts par le filtre automatique : organizationId doit apparaître dans le where, à la main, vérifiable en revue de code.
findFirst, jamais findUnique
findUnique n'accepte que des champs uniques dans son where : le filtre d'organisation ne peut pas s'y ajouter. La règle est simple et se grep.
Sous-entités via le parent
Une ligne de facture n'a pas d'organizationId direct : le filtre passe par la relation vers la facture, jamais par l'ID de la ligne seul.
IDOR : NOT_FOUND, jamais FORBIDDEN
Une ressource d'une autre organisation renvoie une absence, pas un refus. Un refus confirme qu'un ID valide existe ailleurs.
RBAC en matrice
L'isolation protège les frontières entre organisations. Une matrice de permissions par rôle protège les frontières à l'intérieur d'une organisation.
Ce que l'isolation couvre, ce qu'elle ne couvre pas
La plupart des pages produit s'arrêtent au premier tableau. Voici le second.
Ce que ctx.orgDb filtre
- Les lectures sur les modèles enregistrés comme scopés par organisation
- findMany, findFirst, findFirstOrThrow, count, aggregate, groupBy
- De façon identique dans tous les routers qui utilisent ctx.orgDb
Ce qui reste à votre charge
- Toutes les écritures : create, update, delete
- findUnique, volontairement hors du filtre automatique
- Enregistrer un nouveau modèle comme scopé par organisation
- Les accès directs via ctx.db (webhooks, tâches planifiées, administration)
Les tests fournis, pas une promesse
Douze fichiers de tests de sécurité livrés avec le code, pas une checklist à écrire soi-même.
Isolation croisée
Deux organisations mockées, chaque lecture sensible (factures, devis, conversations) vérifiée séparément.
IDOR par domaine
Un fichier dédié par surface sensible : comptabilité, conversations, messages, notifications, événements d'intégration tierce.
Audit qui parcourt le schéma
Un test générique vérifie que chaque modèle scopé passe par le filtre automatique, pas seulement les modèles déjà couverts à la main.
Pour qui, et pour qui non
- Vous construisez un SaaS B2B où une organisation a plusieurs utilisateurs
- Une fuite de données entre deux clients serait un incident, pas un détail
- Vous voulez des tests fournis, pas une architecture à concevoir seul
- Votre produit est mono-tenant, sans notion d'organisation
- Vous préférez une isolation par base de données séparée par client
Le code d'isolation, pas juste l'idée.
Prisma, tRPC, les tests de sécurité : tout est dans le repo, dès le premier jour.