Target Keyword: "multi-tenant saas architecture ai"
Tags: saas,architecture,ai,programming,developer
Type: Guide
Content
Building Multi-Tenant SaaS with AI: Architecture Patterns for 2026
Building a SaaS product that serves multiple customers (multi-tenancy) while integrating AI capabilities requires careful architecture. Here's the complete guide to building a scalable, secure multi-tenant AI SaaS.
What Is Multi-Tenancy?
In a multi-tenant system, one application instance serves multiple customers (tenants), each with their own data and settings. This is how products like Salesforce, HubSpot, and Notion work.
Benefits:
- Cost efficiency (shared infrastructure)
- Easier maintenance (one codebase)
- Fast onboarding (new tenant = new row in database)
Database Architecture
# Option 1: Shared database, shared schema (most common)
class TenantAwareModel:
"""All models inherit from this for tenant isolation."""
tenant_id: int
@classmethod
def get_for_tenant(cls, tenant_id, **filters):
return cls.query.filter_by(tenant_id=tenant_id, **filters)
class User(TenantAwareModel, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
tenant_id = db.Column(db.Integer, db.ForeignKey('tenants.id'), nullable=False)
email = db.Column(db.String(255), unique=True, nullable=False)
# ...
# Always filter by tenant_id
def get_users(tenant_id):
return User.query.filter_by(tenant_id=tenant_id).all()
Tenant Isolation Middleware
from flask import g, request
@app.before_request
def before_request():
"""Extract and validate tenant from request."""
# Get tenant from subdomain, header, or JWT
host = request.host
if host.startswith('app.'):
subdomain = host.split('.')[0]
tenant = Tenant.query.filter_by(slug=subdomain).first()
else:
# API calls use header
tenant_id = request.headers.get('X-Tenant-ID')
tenant = Tenant.query.get(tenant_id)
if not tenant:
abort(403, "Invalid tenant")
g.tenant = tenant
g.tenant_id = tenant.id
AI Service Per Tenant
class TenantAIClient:
"""AI client configured per tenant."""
def __init__(self, tenant_id: int):
self.tenant_id = tenant_id
self.tenant = Tenant.query.get(tenant_id)
self.config = self.tenant.ai_config
def chat(self, messages: list[dict]) -> str:
"""Call AI with tenant-specific settings."""
# Apply tenant-specific system prompt modifications
enhanced_messages = self._apply_tenant_context(messages)
# Use tenant's own API key or shared pool
api_key = self.tenant.ai_api_key or AI_POOL.get_key()
return call_ai(api_key, enhanced_messages)
def _apply_tenant_context(self, messages):
"""Inject tenant-specific context into prompts."""
if not self.config.get('inject_context'):
return messages
context = f"""
Tenant: {self.tenant.name}
Plan: {self.tenant.plan}
Custom Instructions: {self.tenant.ai_instructions or 'None'}
"""
# Prepend to first user message
messages = list(messages)
if messages and messages[0]['role'] == 'user':
messages[0]['content'] = context + "\n\n" + messages[0]['content']
return messages
Tenant-Specific AI Features
class TenantFeatureFlags:
FEATURES = {
'basic_chat': {'tier': 'free', 'limit': 100},
'code_review': {'tier': 'pro', 'limit': 1000},
'document_analysis': {'tier': 'pro', 'limit': 500},
'custom_prompts': {'tier': 'enterprise', 'limit': None},
}
@classmethod
def is_enabled(cls, tenant, feature: str) -> bool:
tenant_tier = TIER_ORDER.index(tenant.plan)
feature_tier = TIER_ORDER.index(cls.FEATURES[feature]['tier'])
return tenant_tier >= feature_tier
@classmethod
def check_limit(cls, tenant, feature: str) -> bool:
limit = cls.FEATURES[feature]['limit']
if limit is None:
return True
usage = UsageLog.query.filter_by(
tenant_id=tenant.id,
feature=feature
).count()
return usage < limit
Usage Tracking and Billing
class UsageTracker:
def track(self, tenant_id: int, feature: str, tokens_used: int, cost_usd: float):
log = UsageLog(
tenant_id=tenant_id,
feature=feature,
tokens_used=tokens_used,
cost_usd=cost_usd,
timestamp=datetime.utcnow()
)
db.session.add(log)
# Update tenant's running total
tenant = Tenant.query.get(tenant_id)
tenant.current_period_cost += cost_usd
# Check if over budget
if tenant.monthly_budget and tenant.current_period_cost > tenant.monthly_budget:
self._notify_over_budget(tenant)
db.session.commit()
def get_tenant_usage(self, tenant_id: int, period: str = 'month') -> dict:
start = self._get_period_start(period)
logs = UsageLog.query.filter(
UsageLog.tenant_id == tenant_id,
UsageLog.timestamp >= start
).all()
return {
'total_cost': sum(log.cost_usd for log in logs),
'total_tokens': sum(log.tokens_used for log in logs),
'by_feature': self._group_by_feature(logs)
}
Onboarding New Tenants
@app.route('/api/tenants', methods=['POST'])
def create_tenant():
data = request.get_json()
# Create tenant record
tenant = Tenant(
name=data['company_name'],
slug=data['slug'],
plan='free',
owner_email=data['email']
)
db.session.add(tenant)
db.session.flush() # Get tenant.id
# Create owner user
user = User(
tenant_id=tenant.id,
email=data['email'],
role='owner',
hashed_password=hash_password(data['password'])
)
db.session.add(user)
# Set up default AI settings
ai_config = AIConfig(
tenant_id=tenant.id,
model='claude-3-5-sonnet-20241022',
max_tokens=1024,
temperature=0.7
)
db.session.add(ai_config)
db.session.commit()
# Send welcome email
send_welcome_email(tenant, user)
return {"tenant_id": tenant.id, "slug": tenant.slug}, 201
Security Best Practices
# 1. Always validate tenant ownership
def get_tenant_resource(resource_id, tenant_id):
resource = Resource.query.get(resource_id)
if resource.tenant_id != tenant_id:
abort(403, "Access denied")
return resource
# 2. Never rely solely on URL params for tenant ID
# ✅ Good: Use g.tenant_id from middleware
# ❌ Bad: tenant_id = request.args.get('tenant_id')
# 3. Sanitize AI prompts to prevent cross-tenant data leakage
def sanitize_tenant_prompt(tenant_id, user_input):
# Block attempts to extract other tenants' data
blocked = ['tenant_id', 'other_tenant', 'cross_tenant']
for phrase in blocked:
if phrase in user_input.lower():
return False, "Invalid input"
return True, user_input
Getting Started
Build your multi-tenant AI SaaS with ofox.ai — their API supports multi-tenant usage tracking and provides reliable infrastructure for production AI applications.
This article contains affiliate links.
Tags: saas,architecture,ai,programming,developer
Canonical URL: https://dev.to/zny10289
Top comments (0)