Introduction
Le multi-tenancy permet à une même application de servir plusieurs clients (tenants) tout en isolant leurs données. Cette approche est essentielle pour les SaaS afin de réduire les coûts et simplifier la maintenance. Au lieu de déployer une instance par client, on utilise un identifiant de tenant pour filtrer les accès. Ce tutoriel vous guide pas à pas pour implémenter une solution simple et sécurisée avec Prisma et TypeScript.
Prérequis
- Node.js 20+
- Connaissances de base en TypeScript
- PostgreSQL installé
- npm ou yarn
Initialisation du projet
mkdir multi-tenancy-tutorial
cd multi-tenancy-tutorial
npm init -y
npm install express prisma @prisma/client
npx prisma initCette commande crée le projet et installe les dépendances nécessaires. Prisma gérera le schéma de base de données avec le champ tenantId pour l'isolation.
Schéma Prisma avec tenant
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
email String @unique
tenantId String
name String
}Le modèle User inclut un tenantId obligatoire. Chaque requête filtrera automatiquement sur ce champ pour isoler les données par client.
Middleware d'isolation
import { Request, Response, NextFunction } from 'express';
export const tenantMiddleware = (req: Request, res: Response, next: NextFunction) => {
const tenantId = req.headers['x-tenant-id'] as string;
if (!tenantId) {
return res.status(400).json({ error: 'Tenant ID requis' });
}
(req as any).tenantId = tenantId;
next();
};Ce middleware extrait le tenantId depuis les headers et l'attache à la requête. Il garantit que chaque appel est associé à un tenant valide.
Configuration Prisma avec tenant
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
export const getTenantPrisma = (tenantId: string) => {
return prisma.$extends({
query: {
user: {
findMany: ({ args }) => {
args.where = { ...args.where, tenantId };
return args;
}
}
}
});
};Cette extension Prisma injecte automatiquement le filtre tenantId sur les requêtes. Elle évite d'oublier le filtre manuellement et renforce la sécurité.
Route exemple avec isolation
import express from 'express';
import { tenantMiddleware } from '../middleware/tenant';
import { getTenantPrisma } from '../prisma';
const router = express.Router();
router.use(tenantMiddleware);
router.get('/', async (req, res) => {
const tenantId = (req as any).tenantId;
const prismaTenant = getTenantPrisma(tenantId);
const users = await prismaTenant.user.findMany();
res.json(users);
});
export default router;La route applique le middleware puis utilise l'instance Prisma étendue. Les résultats sont automatiquement limités au tenant courant.
Bonnes pratiques
- Toujours valider le tenantId en entrée
- Utiliser des extensions Prisma pour centraliser la logique
- Tester l'isolation avec plusieurs tenants
- Ajouter des index sur tenantId pour les performances
- Logger les accès par tenant pour l'audit
Erreurs courantes
- Oublier le filtre tenantId dans une requête manuelle
- Stocker le tenantId dans le corps au lieu des headers
- Ne pas gérer les cas où le tenantId est absent
- Ignorer les performances sur les tables volumineuses sans index
Pour aller plus loin
Approfondissez le sujet avec notre formation dédiée au multi-tenancy avancé. Découvrez nos formations Learni.