# Multi-Tenant Data Isolation

## User Intent

"How do I isolate data for multiple tenants/users in my SaaS application?"

## Operation

**Concept**: Per-user data isolation within a single Graphlit project\
**SDK Method**: `createUser()` + `userId` parameter in client initialization\
**Use Case**: SaaS applications with multiple users/tenants

***

## Multi-Tenant Pattern (Recommended)

### Strategy: Per-User Scoping in Single Project

Graphlit supports multi-tenancy by creating users within your project and scoping SDK operations to each user.

```typescript
import { Graphlit, Types } from 'graphlit-client';

// 1. Admin client (no user scoping) for user management
const adminClient = new Graphlit({
  organizationId: process.env.GRAPHLIT_ORGANIZATION_ID!,
  environmentId: process.env.GRAPHLIT_ENVIRONMENT_ID!,
  jwtSecret: process.env.GRAPHLIT_JWT_SECRET!,
});

// 2. When a user signs up (e.g., via Clerk, Supabase, Auth0)
async function onUserSignUp(authUser: { id: string; name: string; email: string }) {
  // Create user in Graphlit (maps to your auth provider's user)
  const user = await adminClient.createUser({
    name: authUser.name,
    identifier: authUser.id,  // Your auth provider's user ID (Clerk, Supabase, etc.)
    type: Types.UserTypes.Human,
  });
  
  // Store user.createUser.id in your database alongside auth user
  await db.users.update(authUser.id, {
    graphlitUserId: user.createUser.id
  });
}

// 3. On each user request, create scoped client
async function handleUserRequest(authUserId: string, request: any) {
  // Get Graphlit user ID from your database
  const graphlitUserId = await db.users.getGraphlitUserId(authUserId);
  
  // Create user-scoped client
  const userClient = new Graphlit({
    organizationId: process.env.GRAPHLIT_ORGANIZATION_ID!,
    environmentId: process.env.GRAPHLIT_ENVIRONMENT_ID!,
    jwtSecret: process.env.GRAPHLIT_JWT_SECRET!,
    userId: graphlitUserId,  // ← ALL operations now scoped to this user
  });
  
  // All content, conversations, and feeds are automatically isolated
  const results = await userClient.queryContents({
    search: request.query,
  });
  
  return results;  // Only returns this user's content
}
```

***

## What Gets Scoped?

When you pass `userId` to the Graphlit client:

**User-Scoped (Isolated per User)**:

* ✅ **Content** - Documents, files, web pages ingested by this user
* ✅ **Conversations** - Chat history and AI interactions
* ✅ **Feeds** - Connected data sources (Slack, Gmail, etc.)

**Project-Scoped (Shared Across Users)**:

* 📋 **Specifications** - AI model configurations
* 📋 **Workflows** - Content processing pipelines

This means you define AI models and workflows once at the project level, and each user gets isolated data automatically.

***

## Complete Example: Multi-Tenant SaaS

```typescript
import { Graphlit, Types } from 'graphlit-client';

export class GraphlitService {
  private adminClient: Graphlit;
  
  constructor() {
    // Admin client for user management
    this.adminClient = new Graphlit({
      organizationId: process.env.GRAPHLIT_ORGANIZATION_ID!,
      environmentId: process.env.GRAPHLIT_ENVIRONMENT_ID!,
      jwtSecret: process.env.GRAPHLIT_JWT_SECRET!,
    });
  }
  
  // Create user on signup
  async createUser(authUser: { id: string; name: string; email: string }) {
    // Check if user already exists
    try {
      const existing = await this.adminClient.getUserByIdentifier(authUser.id);
      if (existing.userByIdentifier) {
        return existing.userByIdentifier.id;
      }
    } catch (error) {
      // User doesn't exist, create new one
    }
    
    const user = await this.adminClient.createUser({
      name: authUser.name,
      identifier: authUser.id,  // Clerk/Supabase/Auth0 user ID
      type: Types.UserTypes.Human,
    });
    
    return user.createUser.id;
  }
  
  // Get user-scoped client
  getUserClient(graphlitUserId: string): Graphlit {
    return new Graphlit({
      organizationId: process.env.GRAPHLIT_ORGANIZATION_ID!,
      environmentId: process.env.GRAPHLIT_ENVIRONMENT_ID!,
      jwtSecret: process.env.GRAPHLIT_JWT_SECRET!,
      userId: graphlitUserId,
    });
  }
}

// Usage in your API routes
const graphlitService = new GraphlitService();

// POST /api/signup
async function handleSignup(req: Request) {
  const { clerkUserId, name, email } = req.body;
  
  // Create Graphlit user
  const graphlitUserId = await graphlitService.createUser({
    id: clerkUserId,
    name,
    email,
  });
  
  // Store in your database
  await db.users.create({
    clerkUserId,
    graphlitUserId,
    name,
    email,
  });
}

// POST /api/documents/upload
async function handleUpload(req: Request) {
  const { userId } = req.auth;  // From your auth middleware
  const { documentUrl } = req.body;
  
  // Get user's Graphlit client
  const dbUser = await db.users.findByClerkId(userId);
  const client = graphlitService.getUserClient(dbUser.graphlitUserId);
  
  // Upload document (automatically scoped to this user)
  const content = await client.ingestUri(
    documentUrl,
    'User Document',
    undefined,
    undefined,
    true
  );
  
  return { contentId: content.ingestUri.id };
}

// POST /api/chat
async function handleChat(req: Request) {
  const { userId } = req.auth;
  const { message, conversationId } = req.body;
  
  const dbUser = await db.users.findByClerkId(userId);
  const client = graphlitService.getUserClient(dbUser.graphlitUserId);
  
  // Chat only accesses this user's content
  const response = await client.promptConversation(
    message,
    conversationId
  );
  
  return { reply: response.promptConversation.message?.message };
}
```

***

## Multi-Tenant Benefits

**Complete Data Isolation**: Each user's content/conversations/feeds are isolated\
**Single Project Management**: One project, shared specifications/workflows\
**Scalable**: Thousands of users per project\
**Auth Integration**: Works with Clerk, Supabase, Auth0, custom auth\
**Usage Tracking**: Per-user usage tracking available

***

## Implementation Checklist

* [ ] Create admin Graphlit client for user management
* [ ] On user signup: `createUser()` with identifier from auth provider
* [ ] Store Graphlit userId in your database alongside auth user
* [ ] On each request: Create user-scoped client with `userId` parameter
* [ ] All content/conversation/feed operations automatically isolated
* [ ] Share specifications and workflows across all users (project-level)

***

## Alternative: Legacy ownerId (Deprecated)

The legacy `ownerId` parameter is still supported but deprecated. Use `userId` for new applications.

***


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.graphlit.dev/api-guides/use-cases/production/production-multi-tenant-isolation.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
