Build Multi-Tenant SaaS the AI-Era Way

LLMs can generate your entire backend because the patterns are consistent and token-efficient.

The Challenge: Multi-Tenant Complexity

Enterprise SaaS requires:

  • Row-level isolation - One database, multiple tenants, zero data bleeding
  • RBAC enforcement - Users, roles, permissions scattered across resolvers
  • Consistent patterns - Every API endpoint needs the same isolation logic
  • AI-generated code - LLMs struggle to learn your custom resolver patterns
  • Performance tuning - Each new relationship needs optimization

Traditional GraphQL answers: write field resolvers for every relationship. This leads to 47 different custom patterns, none of which an LLM can predict.

FraiseQL Solution: Database-First Isolation

You write SQL views. The database handles isolation. Your code becomes simple and repetitive—exactly what LLMs love.

1. Define Tenant Context in Views

PostgreSQL views with tenant filtering:

CREATE VIEW v_user_posts AS
SELECT
  jsonb_build_object(
    'id', p.id::text,
    'title', p.title,
    'author_id', p.author_id::text,
    'created_at', p.created_at
  ) AS data
FROM tb_post p
WHERE p.tenant_id = current_setting('app.tenant_id')::uuid;

2. Set Tenant Context Once

Declare the query. The Rust runtime sets tenant context automatically:

@fraiseql.query(sql_source="v_user_posts")
def user_posts(user_id: ID) -> list[UserPost]:
  # Tenant context is set by the Rust runtime via fraiseql.toml
  # All views enforce it automatically via current_setting('app.tenant_id')
  pass

3. Define Python Types

Simple, repeatable schema:

@fraiseql.type
class UserPost:
  id: ID
  title: str
  author_id: ID
  created_at: str

Result: Every resolver follows the same pattern. 4 core patterns instead of 47 custom ones. LLMs can generate your entire API.

Real-World Example: Team Workspace SaaS

Platform: Organizations have Teams, Teams have Projects, Projects have Tasks

Challenge: Fetch team's projects with task counts

Traditional GraphQL problem: N+1 queries across 3 tables + authorization checks in each resolver

FraiseQL approach:

-- Single SQL view handles everything
CREATE VIEW v_team_projects AS
SELECT
  jsonb_build_object(
    'id', t.id::text,
    'name', t.name,
    'projects', (
      SELECT jsonb_agg(jsonb_build_object(
        'id', p.id::text,
        'name', p.name,
        'task_count', (SELECT count(*) FROM tb_task WHERE project_id = p.id)
      ))
      FROM tb_project p
      WHERE p.team_id = t.id
        AND p.tenant_id = current_setting('app.tenant_id')::uuid
    )
  ) AS data
FROM tb_team t
WHERE t.tenant_id = current_setting('app.tenant_id')::uuid;

One query. One resolver. Done.

@fraiseql.query(sql_source="v_team_projects")
def team_with_projects(team_id: ID) -> TeamProjects:
  pass

Performance Impact

1
Database query
vs. 1 + N + N*M before
~300ms
Full response
vs. 2-5s average
80%
Fewer LLM tokens
Consistent patterns

Advanced Patterns for Enterprise

Multi-Database Tenants

If you use database-per-tenant instead of schema-per-tenant:

@fraiseql.query(sql_source="v_user")
def user_by_tenant(user_id: ID) -> User:
  # Per-tenant database routing is configured in fraiseql.toml
  # The Rust runtime selects the correct connection pool
  pass

Role-Based Access Control

Enforce in views with user role context:

CREATE VIEW v_user_reports AS
SELECT
  jsonb_build_object(
    'id', r.id::text,
    'title', r.title,
    'data', CASE
      WHEN current_setting('app.user_role') = 'admin' THEN r.data
      WHEN current_setting('app.user_role') = 'editor' THEN jsonb_delete(r.data, '{sensitive_metrics}')
      ELSE NULL
    END
  ) AS data
FROM tb_report r
WHERE r.tenant_id = current_setting('app.tenant_id')::uuid;

Audit Logging

Automatic audit trail of what data was accessed:

CREATE TRIGGER audit_user_access AFTER SELECT ON v_user
FOR EACH ROW EXECUTE FUNCTION log_data_access(
  'user_view',
  NEW.id,
  current_setting('app.tenant_id'),
  current_setting('app.user_id')
);

Related Resources

📘 How It Works

Understand FraiseQL's architecture and why database-first isolation is superior.

Read →

💻 For Architects

Design scalable multi-tenant systems with confidence.

Read →

🤖 For AI Engineers

Consistent patterns mean better code generation and LLM integration.

Read →

🏗️ Authorization Sprawl

See how FraiseQL solves authorization complexity at the database level.

Read →

Ready to Scale Your SaaS?

Get started with FraiseQL and build your next multi-tenant platform faster than ever.