architecture multi-tenant pour votre SaaS Node.js avec PostgreSQL

Dans ce tutoriel approfondi, nous allons implémenter une architecture multi-tenant sécurisée et performante pour une application SaaS basée sur Node.js et PostgreSQL. Vous apprendrez à isoler les données, à configurer le filtrage côté base de données et à intégrer tout cela dans votre code grâce à TypeORM.

1. Choisir le mode d’isolation des données

  • Row-based tenancy (colonne tenant_id + Row Level Security)
  • Schema-based tenancy (un schéma par client)

Nous privilégierons ici le « row-based » pour plus de simplicité et une maintenance allégée.

2. Préparer PostgreSQL (RLS et variable de session)

  1. Activer le RLS sur vos tables critiques :
    ALTER TABLE users ENABLE ROW LEVEL SECURITY;
  2. Définir une politique d’isolation :
    CREATE POLICY tenant_isolation
      ON users
      USING (tenant_id = current_setting('app.current_tenant')::int);
  3. Configurer la variable de session dans postgresql.conf :
    custom_variable_classes = 'app'
    app.current_tenant = ''

3. Installer et configurer TypeORM dans Node.js

npm install typeorm pg reflect-metadata
import { DataSource } from 'typeorm';

export const AppDataSource = new DataSource({
  type: 'postgres',
  host: process.env.DB_HOST,
  port: +process.env.DB_PORT,
  username: process.env.DB_USER,
  password: process.env.DB_PASS,
  database: process.env.DB_NAME,
  entities: [__dirname + '/entities/*.js'],
  synchronize: false,
  logging: false,
});

Entité User avec tenant_id

import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  tenant_id: number;

  @Column()
  email: string;

  @Column()
  name: string;
}

4. Middleware Express pour injecter le tenant

import { Request, Response, NextFunction } from 'express';
import { AppDataSource } from './data-source';

export async function tenantMiddleware(req: Request, res: Response, next: NextFunction) {
  const tenantId = req.headers['x-tenant-id'];
  if (!tenantId) return res.status(400).send('Tenant ID missing');

  // Avant chaque requête : définir la variable de session
  await AppDataSource.manager.query(
    `SET app.current_tenant = '${tenantId}';`
  );
  next();
}

Placez ce middleware avant vos routes protégées pour garantir l’isolation.

5. Vérifier l’isolation et mesurer les performances

  • Testez l’accès interdits aux données d’un autre tenant : vous devriez obtenir zéro résultat.
  • Surveillez le plan d’exécution :
    EXPLAIN ANALYZE SELECT * FROM users WHERE tenant_id = 42;
    Indice : créez un index multicolonne sur (tenant_id, id) pour réduire le temps de requête de 30 % en moyenne.

6. Bonnes pratiques et pièges à éviter

  • Ne stockez jamais d’ID d’un tenant dans un token JWT sans vérification côté API.
  • Utilisez un pool de connexions par tenant si vous dépassez 100 tenants pour éviter le « noisy neighbor ».
  • Pensez à archiver les anciens tenants pour alléger la base (partitionnement).

Conclusion

Vous disposez désormais d’une base solide pour déployer une solution multi-tenant dans votre application SaaS. Les bénéfices : isolation forte, montée en charge maîtrisée et évolutivité.

Besoin d’un accompagnement pour industrialiser votre projet ? Contactez notre équipe.