L’interface est localisée ; les présentations et documents des Skills sont traduits automatiquement par IA dans votre langue. Le code, les commandes, chemins et termes techniques restent en version originale, et le document de l’auteur fait foi.
Explorer les Skills
Design et création · SKILL.md

multitenancy

multitenancy

Multi-tenant architecture patterns. Database-per-tenant, schema-per-tenant, shared-schema with tenant ID, row-level security, tenant resolution, and data isolation. USE WHEN: user mentions "multi-tenant", "multitenancy", "SaaS architecture", "tenant isolation", "row-level security", "tenant ID", "subdomain routing" DO NOT USE FOR: general database design …

Independent GitHub snapshot · Automated classification · Original documentation governs

Documentation de la Skill

GitHub

name: multitenancy description: | Multi-tenant architecture patterns. Database-per-tenant, schema-per-tenant, shared-schema with tenant ID, row-level security, tenant resolution, and data isolation.

USE WHEN: user mentions "multi-tenant", "multitenancy", "SaaS architecture", "tenant isolation", "row-level security", "tenant ID", "subdomain routing"

DO NOT USE FOR: general database design - use database skills; authentication - use auth skills allowed-tools: Read, Grep, Glob, Write, Edit

Multi-Tenant Architecture

Isolation Strategies

StrategyIsolationComplexityCost
Database per tenantHighestHighHigh
Schema per tenantHighMediumMedium
Shared schema (tenant_id column)MediumLowLow
Row-level security (RLS)Medium-HighMediumLow

Shared Schema with Tenant ID (most common)

// Middleware: resolve tenant from subdomain or header
function tenantMiddleware(req: Request, res: Response, next: NextFunction) {
  const host = req.hostname; // acme.myapp.com
  const subdomain = host.split('.')[0];
  const tenant = await tenantRepo.findBySubdomain(subdomain);
  if (!tenant) return res.status(404).json({ error: 'Tenant not found' });
  req.tenantId = tenant.id;
  next();
}

// Always filter by tenant
app.get('/api/products', async (req, res) => {
  const products = await db.product.findMany({
    where: { tenantId: req.tenantId },
  });
  res.json(products);
});

Prisma with Tenant Scoping

// Extension to auto-apply tenant filter
const prisma = new PrismaClient().$extends({
  query: {
    $allOperations({ args, query, operation }) {
      if (['findMany', 'findFirst', 'count', 'updateMany', 'deleteMany'].includes(operation)) {
        args.where = { ...args.where, tenantId: getCurrentTenantId() };
      }
      if (['create', 'createMany'].includes(operation)) {
        args.data = { ...args.data, tenantId: getCurrentTenantId() };
      }
      return query(args);
    },
  },
});

PostgreSQL Row-Level Security

-- Enable RLS
ALTER TABLE products ENABLE ROW LEVEL SECURITY;

-- Policy: users see only their tenant's data
CREATE POLICY tenant_isolation ON products
  USING (tenant_id = current_setting('app.tenant_id')::uuid);

-- Set tenant context per request
SET app.tenant_id = 'tenant-uuid-here';
SELECT * FROM products; -- auto-filtered
// Set tenant context on each request
pool.on('connect', async (client) => {
  // Set after getting connection from pool
});

async function withTenant<T>(tenantId: string, fn: () => Promise<T>): Promise<T> {
  const client = await pool.connect();
  try {
    await client.query(`SET app.tenant_id = $1`, [tenantId]);
    return await fn();
  } finally {
    await client.query('RESET app.tenant_id');
    client.release();
  }
}

Schema-Per-Tenant

// Dynamic schema selection
function getTenantSchema(tenantId: string): string {
  return `tenant_${tenantId.replace(/-/g, '_')}`;
}

async function createTenantSchema(tenantId: string) {
  const schema = getTenantSchema(tenantId);
  await db.query(`CREATE SCHEMA IF NOT EXISTS ${schema}`);
  await db.query(`SET search_path TO ${schema}`);
  await runMigrations(); // Apply schema migrations
}

Tenant Resolution Strategies

StrategyExampleBest For
Subdomainacme.myapp.comB2B SaaS
Path prefixmyapp.com/acme/...Simpler setup
Custom headerX-Tenant-ID: acmeAPI-first
JWT claim{ tenantId: "acme" }Authenticated APIs

Anti-Patterns

Anti-PatternFix
No tenant filter on queriesUse middleware or ORM extension to auto-apply
Tenant ID from client without validationDerive from auth token or subdomain
No tenant data isolation testingWrite tests that verify cross-tenant isolation
Shared cache without tenant prefixPrefix all cache keys with tenant ID
No tenant-aware rate limitingRate limit per tenant, not globally

Production Checklist

  • Tenant resolution middleware on all routes
  • Data isolation verified with automated tests
  • Cache keys prefixed with tenant ID
  • Rate limiting per tenant
  • Tenant-scoped background jobs
  • Tenant provisioning and deprovisioning flow
  • Cross-tenant query prevention (RLS or ORM enforcement)
Skills enregistrées