Patterns & Use Cases
Multi-Tenant SaaS
Data isolation per tenant using partition keys
The pattern: Use tenantId as partition key to ensure data isolation.
Why this works:
- Complete isolation between tenants
- All queries automatically scoped to a single tenant
- Prevents cross-tenant data leakage
- Scales as tenants grow
Schema Design
const organizations = container('organizations', {
id: field.string(),
name: field.string(),
plan: field.string(),
settings: field.object({
theme: field.string().default('light'),
features: field.array(field.string())
})
}).partitionKey('id'); // Organization ID as partition key
const users = container('users', {
id: field.string(),
organizationId: field.string(),
email: field.string(),
role: field.string(),
profile: field.object({
name: field.string(),
department: field.string().optional()
})
}).partitionKey('organizationId'); // All users for an org in same partition
const documents = container('documents', {
id: field.string(),
tenantId: field.string(),
name: field.string(),
content: field.string(),
createdBy: field.string(),
createdAt: field.date()
}).partitionKey('tenantId');Querying Tenant Data
// All queries automatically scoped to tenant
async function getDocuments(tenantId: string) {
return await db.documents.findMany({
partitionKey: tenantId,
orderBy: { createdAt: 'desc' }
});
}
async function getOrgUsers(organizationId: string) {
return await db.users.findMany({
partitionKey: organizationId,
where: {
role: 'admin'
},
select: {
id: true,
email: true,
profile: { name: true }
}
});
}Security Best Practices
- Always validate tenant context - Ensure the authenticated user's tenant matches the partition key
- Use middleware - Automatically inject tenant ID from request context
- Prevent cross-tenant access - Never allow user-provided tenant IDs without validation
// Example: Express middleware to enforce tenant isolation
function tenantMiddleware(req: Request, res: Response, next: NextFunction) {
const tenantId = req.user?.organizationId;
if (!tenantId) {
return res.status(401).json({ error: 'Unauthorized' });
}
req.tenantId = tenantId; // Attach to request
next();
}
// Use in routes
app.get('/documents', tenantMiddleware, async (req, res) => {
const documents = await db.documents.findMany({
partitionKey: req.tenantId, // Always use validated tenant ID
orderBy: { createdAt: 'desc' }
});
res.json(documents);
});