**Category:** Backend/Database Architecture
/plugin marketplace add Primadetaautomation/claude-dev-toolkit/plugin install claude-dev-toolkit@primadata-marketplaceThis skill inherits all available tools. When active, it can use any tool Claude has access to.
detailed-patterns.mdscripts/create-tenant-schema.sqlscripts/validate-tenant-isolation.sqlCategory: Backend/Database Architecture Version: 1.0 Progressive Levels: 3 (Minimal ā Detailed ā Full)
Multi-tenant architecture patterns for building SaaS applications with complete data isolation, including:
ā Use when:
ā Don't use when:
| Requirement | Pattern | Security Level |
|---|---|---|
| Complete data isolation | PostgreSQL RLS | š High |
| Public job listings + private management | Hybrid RLS policies | š High |
| Per-tenant feature flags | Feature flags in tenant table | š Medium |
| Cross-tenant user access | Tenant memberships table | š High |
| Audit trail | Created/updated timestamps + user tracking | š Medium |
// Core tenant tables
tenants: {
id: uuid (PK)
name: string
slug: string (unique)
status: enum ('ACTIVE' | 'SUSPENDED' | 'INACTIVE')
plan: enum ('STARTER' | 'GROWTH' | 'ENTERPRISE')
featureFlags: json
}
tenantMemberships: {
id: uuid (PK)
userId: uuid (FK ā users)
tenantId: uuid (FK ā tenants)
role: enum ('VIEWER' | 'MEMBER' | 'MANAGER' | 'ADMIN' | 'OWNER')
status: enum ('ACTIVE' | 'PENDING' | 'SUSPENDED')
}
// All business tables must include:
businessTable: {
// ... existing fields
tenantId: uuid (FK ā tenants) // Add to ALL tables
}
-- Enable RLS on table
ALTER TABLE jobs ENABLE ROW LEVEL SECURITY;
-- Basic tenant isolation policy
CREATE POLICY tenant_isolation_policy ON jobs
FOR ALL
USING (
tenant_id::uuid = current_setting('app.current_tenant_id', true)::uuid
);
// Set tenant context in middleware
async function setTenantContext(db, userId: string, tenantId: string) {
await db.execute(sql`SET app.current_user_id = ${userId}`);
await db.execute(sql`SET app.current_tenant_id = ${tenantId}`);
}
Use /add .claude/skills/multi-tenant-patterns/detailed-patterns.md for:
Use /add .claude/skills/multi-tenant-patterns/scripts/ for:
create-tenant-schema.sql - Complete tenant schema setupadd-tenant-columns.sql - Add tenant_id to existing tablescreate-rls-policies.sql - RLS policy templatesvalidate-tenant-isolation.sql - Test tenant isolationmigrate-to-multi-tenant.sql - Data migration script// DON'T: This fails when RLS blocks joined tables
const job = await db
.select({ job: jobs, user: users })
.from(jobs)
.innerJoin(users, eq(jobs.userId, users.id))
.where(eq(jobs.id, jobId));
// Result: RLS blocks users table ā INNER JOIN returns 0 rows
// DO: Query separately when no tenant context
const [job] = await db
.select()
.from(jobs)
.where(and(eq(jobs.id, jobId), eq(jobs.status, 'OPEN')));
// Fetch related data separately (RLS allows specific fields)
const [user] = await db
.select({ id: users.id, name: users.name }) // Only safe fields
.from(users)
.where(eq(users.id, job.userId));
// DON'T: Never trust client-provided tenant IDs
const tenantId = req.body.tenantId; // ā Dangerous!
await setTenantContext(db, userId, tenantId);
// DO: Always derive tenant from authenticated session
const { userId, tenantId } = await validateSession(req);
await setTenantContext(db, userId, tenantId);
-- Primary: Tenant-first composite indexes
CREATE INDEX jobs_tenant_id_status_idx ON jobs (tenant_id, status);
CREATE INDEX jobs_tenant_id_user_id_idx ON jobs (tenant_id, user_id);
-- Secondary: Tenant-only indexes for filtering
CREATE INDEX jobs_tenant_id_idx ON jobs (tenant_id);
// GOOD: Tenant filter first (uses index efficiently)
WHERE tenant_id = $1 AND status = 'OPEN'
// BAD: Status filter first (full table scan)
WHERE status = 'OPEN' AND tenant_id = $1
Cause: RLS policy blocks access due to missing/invalid tenant context Solution: Check tenant context is set before queries, verify tenant membership
Cause: RLS policy too restrictive, doesn't allow public access Solution: Update policy to allow access when status = 'OPEN' AND no tenant context
Cause: Missing indexes on tenant_id column Solution: Add composite indexes: (tenant_id, frequently_filtered_column)
Cause: Tenant context not set or RLS policy missing Solution: Verify RLS enabled, check session variables are set correctly
backend-development-patterns - API design and database patternssecurity-essentials - Authentication and authorizationtesting-fundamentals - Testing tenant isolationproduction-code-standards - Error handling and loggingProgressive Loading:
Total Skill Budget: ~5600 tokens (well under 10K recommendation)
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.