https://github.com/lanemc/multi-tenant-saas-toolkit
๐ข Complete multi-tenancy solution for Node.js applications with TypeScript support
https://github.com/lanemc/multi-tenant-saas-toolkit
abac authorization express multi-tenant multitenancy nestjs nodejs prisma rbac saas tenant-isolation typescript
Last synced: about 2 months ago
JSON representation
๐ข Complete multi-tenancy solution for Node.js applications with TypeScript support
- Host: GitHub
- URL: https://github.com/lanemc/multi-tenant-saas-toolkit
- Owner: lanemc
- License: mit
- Created: 2025-06-19T14:52:29.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2025-06-19T16:37:14.000Z (about 1 year ago)
- Last Synced: 2025-06-19T16:37:33.412Z (about 1 year ago)
- Topics: abac, authorization, express, multi-tenant, multitenancy, nestjs, nodejs, prisma, rbac, saas, tenant-isolation, typescript
- Language: TypeScript
- Size: 214 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# ๐ข Multi-Tenant SaaS Toolkit
[](https://badge.fury.io/js/@saaskit%2Fmultitenancy-core)
[](http://www.typescriptlang.org/)
[](https://opensource.org/licenses/MIT)
[](http://makeapullrequest.com)
> **The fastest way to add multi-tenancy to your Node.js application** โก
Stop spending weeks implementing multi-tenancy from scratch. Get **production-ready tenant isolation**, **automatic data filtering**, and **enterprise-grade authorization** in minutes, not months.
```bash
# Get started in 30 seconds
npm install @saaskit/multitenancy-core
```
---
## ๐ฏ Why This Toolkit?
**โ Before: The Pain**
- Weeks implementing tenant context
- Manual query filtering everywhere
- Security vulnerabilities
- Inconsistent authorization
- Framework lock-in
- Data leakage risks
**โ
After: Pure Joy**
- Setup in 5 minutes
- Automatic tenant filtering
- Security by default
- Consistent RBAC/ABAC
- Framework agnostic
- Zero data leakage
---
## ๐ 30-Second Quick Start
**1. Install the toolkit:**
```bash
npm install @saaskit/multitenancy-core @saaskit/multitenancy-adapters
```
**2. Add one middleware:**
```typescript
import { createTenantMiddleware } from '@saaskit/multitenancy-core';
app.use(createTenantMiddleware({
resolution: { type: 'subdomain' },
dataStore: yourDataStore // We'll show you how!
}));
```
**3. That's it!** ๐ Your app now has:
- โ
Automatic tenant detection
- โ
Secure context isolation
- โ
Ready for multi-tenancy
---
## ๐ What You Get
๐ Smart Tenant Context - Automatic tenant detection & isolation
- **AsyncLocalStorage magic** - Context follows your requests everywhere
- **Multiple resolution strategies** - Subdomain, header, JWT, or custom
- **Zero performance overhead** - Built for production scale
- **Type-safe everywhere** - Full TypeScript support
๐ Bulletproof Data Isolation - Never leak tenant data again
- **ORM integrations** - Prisma, Sequelize, Mongoose
- **Automatic query filtering** - Set it once, works everywhere
- **Multi-database support** - Separate databases per tenant
- **Admin override** - Safe cross-tenant operations
๐ฅ Enterprise Authorization - RBAC + ABAC in one package
- **Pre-built roles** - Admin, member, viewer out of the box
- **Flexible permissions** - Fine-grained access control
- **Policy engine** - Complex authorization rules made simple
- **Audit logging** - Track every action automatically
๐ฏ Framework Freedom - Works with your favorite stack
- **Express** - Drop-in middleware
- **NestJS** - Decorators and guards
- **Fastify** - High-performance plugins
- **Any Node.js app** - Framework-agnostic core
---
## ๐ Real-World Examples
### ๐ฆ Express + Prisma (Most Popular)
```typescript
// 1. Setup your data store (implement once, use everywhere)
const tenantDataStore = {
async getTenantById(id: string) {
return await prisma.tenant.findUnique({ where: { id } });
},
async getTenantBySubdomain(subdomain: string) {
return await prisma.tenant.findUnique({ where: { subdomain } });
},
async getUserTenant(userId: string, tenantId: string) {
return await prisma.tenantUser.findUnique({
where: { userId_tenantId: { userId, tenantId } },
include: { roles: true }
});
}
};
// 2. Add tenant middleware (handles everything automatically)
app.use(createTenantMiddleware({
resolution: { type: 'subdomain' },
dataStore: tenantDataStore,
allowNoTenant: false // Strict tenant isolation
}));
// 3. Apply Prisma adapter (queries auto-filtered by tenant)
import { applyPrismaAdapter } from '@saaskit/multitenancy-adapters';
applyPrismaAdapter(prisma, {
tenantField: 'tenantId',
models: ['User', 'Project', 'Task', 'Invoice']
});
// 4. Use anywhere - tenant context is automatic!
app.get('/api/projects', async (req, res) => {
// Only returns projects for current tenant - automatically!
const projects = await prisma.project.findMany();
res.json(projects);
});
// 5. Add role-based protection
app.delete('/api/projects/:id',
requireRole('admin', 'project-manager'),
async (req, res) => {
await prisma.project.delete({ where: { id: req.params.id } });
res.json({ success: true });
}
);
```
### ๐ NestJS + TypeORM (Enterprise)
```typescript
// app.module.ts
import { MultitenancyModule } from '@saaskit/multitenancy-nestjs';
@Module({
imports: [
MultitenancyModule.forRoot({
resolution: { type: 'header', headerName: 'x-tenant-id' },
dataStore: TypeOrmTenantDataStore
})
]
})
export class AppModule {}
// projects.controller.ts
@Controller('projects')
@UseGuards(TenantAuthGuard)
export class ProjectsController {
@Get()
@Roles('member', 'admin')
async findAll(@TenantContext() context: TenantContext) {
// Automatically filtered by tenant
return this.projectsService.findAll();
}
@Delete(':id')
@Permissions('projects:delete')
async remove(@Param('id') id: string) {
return this.projectsService.remove(id);
}
}
```
### โก Fastify + Mongoose (High Performance)
```typescript
// Register the plugin
await fastify.register(require('@saaskit/multitenancy-fastify'), {
resolution: { type: 'subdomain' },
dataStore: mongoTenantDataStore
});
// Apply Mongoose plugin globally
import { mongooseTenantPlugin } from '@saaskit/multitenancy-adapters';
mongoose.plugin(mongooseTenantPlugin, {
tenantField: 'tenantId',
indexTenant: true // Automatic indexing
});
// Use in routes
fastify.get('/api/users', {
preHandler: [fastify.requireTenant, fastify.requireRole('admin')]
}, async (request, reply) => {
// Auto-filtered by tenant
const users = await User.find();
return users;
});
```
---
## ๐จ Tenant Resolution Strategies
Choose the strategy that fits your architecture:
Strategy
Use Case
Example
Setup
๐ Subdomain
Customer-facing SaaS
acme.yoursaas.com
```typescript
{
resolution: {
type: 'subdomain'
}
}
```
๐ Header
API-first, mobile apps
X-Tenant-ID: acme
```typescript
{
resolution: {
type: 'header',
headerName: 'x-tenant-id'
}
}
```
๐ซ JWT Token
Microservices, SPAs
{ tenantId: "acme" }
```typescript
{
resolution: {
type: 'token',
tokenClaim: 'tenantId'
}
}
```
โ๏ธ Custom
Complex routing logic
API key, path, etc.
```typescript
{
resolution: {
type: 'custom',
customResolver: async (req) => {
const apiKey = req.headers['x-api-key'];
const client = await getClientByApiKey(apiKey);
return client?.tenantId;
}
}
}
```
---
## ๐ Authorization Made Simple
### Quick Role Setup
```typescript
import { RoleManager } from '@saaskit/multitenancy-auth';
const roleManager = new RoleManager({
roles: [
{
name: 'admin',
permissions: [
'users:*', // All user operations
'projects:*', // All project operations
'billing:*', // Billing management
'settings:*' // Tenant settings
]
},
{
name: 'project-manager',
permissions: [
'users:read',
'projects:*', // Full project access
'tasks:*'
]
},
{
name: 'member',
permissions: [
'users:read',
'projects:read',
'projects:write', // Can edit projects
'tasks:*'
]
},
{
name: 'viewer',
permissions: [
'users:read',
'projects:read',
'tasks:read'
]
}
]
});
```
### Smart Permission Hierarchies
```typescript
// Set up permission inheritance
roleManager.setPermissionHierarchy({
'users:manage': ['users:read', 'users:write', 'users:delete'],
'projects:manage': ['projects:read', 'projects:write', 'projects:delete'],
'admin:*': ['users:manage', 'projects:manage', 'billing:manage']
});
// Now 'admin:*' automatically includes all sub-permissions!
```
### Advanced ABAC Policies
```typescript
import { PolicyEngine } from '@saaskit/multitenancy-auth';
const policyEngine = new PolicyEngine();
// Resource ownership policy
policyEngine.registerPolicy({
id: 'resource-ownership',
name: 'Users can manage their own resources',
effect: 'allow',
actions: ['read', 'write', 'delete'],
resources: ['project', 'task', 'document'],
conditions: [{
attribute: 'resource.attributes.ownerId',
operator: 'eq',
value: '${subject.id}' // Dynamic value
}]
});
// Time-based access policy
policyEngine.registerPolicy(
PolicyEngine.createTimeBasedPolicy(
'business-hours',
'Allow admin access only during business hours',
['admin:*'],
['*'],
{ start: '09:00', end: '17:00', timezone: 'UTC' }
)
);
// IP-based restrictions
policyEngine.registerPolicy({
id: 'ip-whitelist',
name: 'Restrict admin access to office IPs',
effect: 'allow',
actions: ['admin:*'],
resources: ['*'],
conditions: [{
attribute: 'environment.ipAddress',
operator: 'in',
value: ['192.168.1.0/24', '10.0.0.0/8']
}]
});
```
---
## ๐ Database Integration
### Prisma (Recommended)
```typescript
// schema.prisma
model User {
id String @id @default(cuid())
email String
tenantId String // Required field
@@unique([email, tenantId])
@@index([tenantId])
}
model Project {
id String @id @default(cuid())
name String
tenantId String // Required field
ownerId String
owner User @relation(fields: [ownerId], references: [id])
@@index([tenantId])
@@index([tenantId, ownerId])
}
```
```typescript
// Apply the adapter
import { applyPrismaAdapter } from '@saaskit/multitenancy-adapters';
applyPrismaAdapter(prisma, {
tenantField: 'tenantId',
models: ['User', 'Project', 'Task'], // Auto-filtered models
exclude: ['SystemLog'], // Skip certain models
onViolation: 'throw' // or 'warn' for development
});
// All queries now automatically filtered by tenant!
const users = await prisma.user.findMany(); // Only current tenant's users
const projects = await prisma.project.findMany(); // Only current tenant's projects
```
### Sequelize
```typescript
// models/User.js
import { DataTypes } from 'sequelize';
import { applySequelizeAdapter } from '@saaskit/multitenancy-adapters';
const User = sequelize.define('User', {
email: DataTypes.STRING,
tenantId: {
type: DataTypes.STRING,
allowNull: false
}
});
// Apply tenant filtering
applySequelizeAdapter(User, {
tenantField: 'tenantId',
autoScope: true
});
// Usage - automatically scoped to tenant
const users = await User.findAll(); // Only current tenant's users
```
### Mongoose
```typescript
// models/User.js
import mongoose from 'mongoose';
import { mongooseTenantPlugin } from '@saaskit/multitenancy-adapters';
const userSchema = new mongoose.Schema({
email: String,
name: String
// tenantId added automatically by plugin
});
// Apply the plugin
userSchema.plugin(mongooseTenantPlugin, {
tenantField: 'tenantId',
indexTenant: true,
autoPopulate: true
});
const User = mongoose.model('User', userSchema);
// Usage - automatically scoped
const users = await User.find(); // Only current tenant's users
```
---
## ๐ Advanced Patterns
### Multi-Database Architecture
Perfect for enterprise customers who need dedicated databases:
```typescript
import { TenantConnectionManager } from '@saaskit/multitenancy-adapters';
const connectionManager = new TenantConnectionManager({
default: process.env.DEFAULT_DATABASE_URL,
resolver: async (tenantId: string) => {
const tenant = await getTenant(tenantId);
return tenant.dedicatedDb ? tenant.databaseUrl : 'default';
},
pooling: {
max: 10,
idleTimeout: 30000
}
});
// Use in your middleware
app.use(async (req, res, next) => {
const tenantId = getCurrentTenantId();
const connection = await connectionManager.getConnection(tenantId);
req.db = connection;
next();
});
```
### Tenant Lifecycle Management
```typescript
import { TenantManager } from '@saaskit/multitenancy-core';
const tenantManager = new TenantManager({
onCreate: async (tenant) => {
// Setup default data, send welcome email, etc.
await setupDefaultData(tenant.id);
await sendWelcomeEmail(tenant.ownerEmail);
},
onSuspend: async (tenant) => {
// Cleanup resources, notify users
await cleanupResources(tenant.id);
},
onDelete: async (tenant) => {
// Full cleanup, data export, etc.
await exportTenantData(tenant.id);
await deleteTenantData(tenant.id);
}
});
// Create a new tenant
const newTenant = await tenantManager.create({
name: 'Acme Corp',
subdomain: 'acme',
plan: 'professional',
ownerEmail: 'admin@acme.com'
});
```
### Audit Logging
Track everything automatically:
```typescript
import { AuditLogger } from '@saaskit/multitenancy-core';
const auditLogger = AuditLogger.getInstance({
store: new DatabaseAuditStore(prisma),
sensitiveFields: ['password', 'apiKey', 'secret']
});
// Automatic logging with decorator
@AuditLog('user:update')
async function updateUser(id: string, data: any) {
return await prisma.user.update({
where: { id },
data
});
}
// Manual logging
await auditLogger.log({
action: 'project:create',
resource: { type: 'project', id: project.id },
result: 'success',
metadata: { name: project.name }
});
// Query audit logs
const recentActivity = await auditLogger.query({
actions: ['user:login', 'user:logout'],
dateRange: { start: yesterday, end: now },
limit: 100
});
```
---
## ๐ Troubleshooting & FAQ
๐ Tenant not detected / Context is undefined
**Common causes:**
1. Middleware not registered or registered after routes
2. Async context lost in callbacks
3. Missing tenant data in resolution strategy
**Solutions:**
```typescript
// โ
Correct: Register middleware BEFORE routes
app.use(createTenantMiddleware(config));
app.use('/api', apiRoutes);
// โ Wrong: Middleware after routes
app.use('/api', apiRoutes);
app.use(createTenantMiddleware(config));
// โ
Preserve async context in callbacks
import { tenantContext } from '@saaskit/multitenancy-core';
setTimeout(() => {
// Use runAsync to preserve context
tenantContext.runAsync(currentContext, async () => {
const tenant = tenantContext.getCurrentTenant();
// ... your code
});
}, 1000);
```
โก Performance Issues
**Optimization tips:**
1. **Enable connection pooling:**
```typescript
applyPrismaAdapter(prisma, {
tenantField: 'tenantId',
models: ['User', 'Project'],
caching: {
enabled: true,
ttl: 300 // 5 minutes
}
});
```
2. **Add database indexes:**
```sql
-- Always index tenant fields
CREATE INDEX idx_users_tenant_id ON users(tenant_id);
CREATE INDEX idx_projects_tenant_id ON projects(tenant_id);
-- Composite indexes for common queries
CREATE INDEX idx_projects_tenant_owner ON projects(tenant_id, owner_id);
```
3. **Use tenant-aware caching:**
```typescript
import { TenantCache } from '@saaskit/multitenancy-core';
const cache = new TenantCache(redisClient);
// Automatically scoped to current tenant
await cache.set('user-preferences', preferences);
const cached = await cache.get('user-preferences');
```
๐ Data Leakage Prevention
**Best practices:**
1. **Always validate tenant access:**
```typescript
app.get('/api/projects/:id', async (req, res) => {
const project = await prisma.project.findUnique({
where: { id: req.params.id }
});
// โ
Double-check tenant ownership
const currentTenant = tenantContext.getCurrentTenantId();
if (project.tenantId !== currentTenant) {
return res.status(404).json({ error: 'Project not found' });
}
res.json(project);
});
```
2. **Use strict mode:**
```typescript
app.use(createTenantMiddleware({
resolution: { type: 'subdomain' },
dataStore: tenantDataStore,
allowNoTenant: false, // โ
Strict: Reject requests without tenant
validateAccess: true // โ
Validate user belongs to tenant
}));
```
3. **Enable audit logging:**
```typescript
// Track all data access
@AuditLog('data:access')
async function getData() {
// Your data access code
}
```
๐งช Testing Multi-Tenant Code
```typescript
import { TenantContextManager } from '@saaskit/multitenancy-core';
describe('Multi-tenant API', () => {
const tenantContext = TenantContextManager.getInstance();
it('should isolate data by tenant', async () => {
// Setup test tenants
const tenant1 = { id: 'tenant1', name: 'Acme Corp' };
const tenant2 = { id: 'tenant2', name: 'Globex Corp' };
// Test with tenant1 context
await tenantContext.runAsync({ tenant: tenant1 }, async () => {
const projects = await getProjects();
expect(projects).toHaveLength(3); // tenant1 has 3 projects
});
// Test with tenant2 context
await tenantContext.runAsync({ tenant: tenant2 }, async () => {
const projects = await getProjects();
expect(projects).toHaveLength(1); // tenant2 has 1 project
});
});
it('should enforce role-based access', async () => {
const memberContext = {
tenant: { id: 'tenant1' },
user: { id: 'user1' },
roles: ['member']
};
await tenantContext.runAsync(memberContext, async () => {
await expect(deleteProject('project1')).rejects.toThrow('Insufficient permissions');
});
});
});
```
---
## ๐ Migration Guides
### From Manual Multi-Tenancy
Step-by-step migration from custom solution
**Before: Manual tenant filtering**
```typescript
// โ Before: Manual and error-prone
app.get('/api/users', async (req, res) => {
const tenantId = req.headers['x-tenant-id']; // Manual extraction
if (!tenantId) return res.status(400).json({ error: 'Missing tenant' });
const users = await prisma.user.findMany({
where: { tenantId: tenantId } // Manual filtering - easy to forget!
});
res.json(users);
});
```
**After: Automatic with SaaS Toolkit**
```typescript
// โ
After: Automatic and bulletproof
app.use(createTenantMiddleware({
resolution: { type: 'header', headerName: 'x-tenant-id' },
dataStore: tenantDataStore
}));
applyPrismaAdapter(prisma, {
tenantField: 'tenantId',
models: ['User'] // Automatic filtering
});
app.get('/api/users', async (req, res) => {
const users = await prisma.user.findMany(); // Automatically filtered!
res.json(users);
});
```
**Migration steps:**
1. Install the toolkit
2. Replace manual tenant extraction with middleware
3. Apply ORM adapters
4. Remove manual filtering from queries
5. Add role-based authorization
6. Test thoroughly
### From Other Multi-Tenancy Libraries
Migration guides for popular alternatives
**From `@clerk/backend`:**
```typescript
// Before
import { clerkMiddleware } from '@clerk/backend';
app.use(clerkMiddleware);
// After: More control and flexibility
import { createTenantMiddleware } from '@saaskit/multitenancy-core';
app.use(createTenantMiddleware({
resolution: { type: 'token', tokenClaim: 'org_id' },
dataStore: clerkTenantDataStore
}));
```
**From `@casl/ability`:**
```typescript
// Before: Manual ability setup for each request
const ability = defineAbilityFor(user);
if (ability.cannot('delete', 'Project')) {
throw new ForbiddenError();
}
// After: Automatic context-aware authorization
import { requirePermission } from '@saaskit/multitenancy-core';
app.delete('/projects/:id', requirePermission('projects:delete'), handler);
```
---
## ๐ฏ Production Checklist
Before going live, ensure you have:
- [ ] **Tenant isolation tested** - Verify no data leakage between tenants
- [ ] **Database indexes added** - Index all `tenantId` fields for performance
- [ ] **Error handling configured** - Proper error responses for invalid tenants
- [ ] **Audit logging enabled** - Track all sensitive operations
- [ ] **Rate limiting per tenant** - Prevent resource exhaustion
- [ ] **Backup strategy** - Tenant-aware backup and restore
- [ ] **Monitoring set up** - Track tenant-specific metrics
- [ ] **Security review completed** - Regular security audits
### Production Configuration
```typescript
// production.ts
const config = {
tenant: {
resolution: { type: 'subdomain' },
dataStore: new CachedTenantDataStore(redis, database),
allowNoTenant: false,
validateAccess: true,
onError: (error, req, res) => {
logger.error('Tenant resolution failed', {
error: error.message,
ip: req.ip,
userAgent: req.get('user-agent')
});
res.status(400).json({ error: 'Invalid tenant configuration' });
}
},
database: {
pooling: { max: 20, idleTimeout: 30000 },
caching: { enabled: true, ttl: 300 },
indexing: { autoCreate: false } // Create indexes manually in production
},
audit: {
enabled: true,
store: new DatabaseAuditStore(prisma),
retention: { days: 365 },
sensitive: ['password', 'apiKey', 'token', 'secret']
},
security: {
rateLimit: {
windowMs: 15 * 60 * 1000, // 15 minutes
max: 1000, // Per tenant
keyGenerator: (req) => `${req.tenant?.id}:${req.ip}`
}
}
};
```
---
## ๐ API Reference
### Core Package
#### `createTenantMiddleware(options: TenantMiddlewareOptions)`
Creates middleware for automatic tenant resolution and context management.
```typescript
interface TenantMiddlewareOptions {
resolution: TenantResolutionOptions;
dataStore: TenantDataStore;
onError?: (error: Error, req: any, res: any) => void;
allowNoTenant?: boolean;
validateAccess?: boolean;
}
```
#### `tenantContext: TenantContextManager`
Singleton for accessing current tenant context.
```typescript
// Get current tenant
const tenant = tenantContext.getCurrentTenant();
const tenantId = tenantContext.getCurrentTenantId();
// Get current user and roles
const user = tenantContext.getCurrentUser();
const roles = tenantContext.getCurrentRoles();
const permissions = tenantContext.getCurrentPermissions();
// Check permissions
const canEdit = tenantContext.hasPermission('projects:write');
const isAdmin = tenantContext.hasRole('admin');
```
#### Middleware Helpers
```typescript
// Require authentication
app.use(requireTenantAuth);
// Require specific roles (any of)
app.use(requireRole('admin', 'moderator'));
// Require specific permissions (any of)
app.use(requirePermission('users:read', 'users:write'));
```
### Adapters Package
#### Prisma Adapter
```typescript
import { applyPrismaAdapter, createPrismaAdapter } from '@saaskit/multitenancy-adapters';
// Apply to existing client
applyPrismaAdapter(prisma, {
tenantField: 'tenantId',
models: ['User', 'Project'],
exclude: ['SystemLog'],
onViolation: 'throw' | 'warn' | 'ignore'
});
// Create new tenant-aware client
const TenantPrismaClient = createTenantPrismaClient(PrismaClient, options);
const prisma = new TenantPrismaClient();
```
#### Sequelize Adapter
```typescript
import { applySequelizeAdapter, TenantModel } from '@saaskit/multitenancy-adapters';
// Apply to specific model
applySequelizeAdapter(User, {
tenantField: 'tenantId',
autoScope: true
});
// Use base model class
class User extends TenantModel {
// Your model definition
}
```
#### Mongoose Adapter
```typescript
import { mongooseTenantPlugin, createTenantModel } from '@saaskit/multitenancy-adapters';
// Apply as plugin
userSchema.plugin(mongooseTenantPlugin, {
tenantField: 'tenantId',
indexTenant: true,
autoPopulate: false
});
// Create tenant-specific models
const TenantUser = createTenantModel(User, tenantId);
```
### Auth Package
#### `RoleManager`
```typescript
const roleManager = new RoleManager({
roles: RoleDefinition[],
permissionHierarchy: Record
});
// Role management
roleManager.registerRole(role);
roleManager.getRole(name);
roleManager.getRolePermissions(roleName);
roleManager.hasRole(name);
// Permission management
roleManager.setPermissionHierarchy(hierarchy);
roleManager.addPermissionImplication(parent, children);
```
#### `PolicyEngine`
```typescript
const policyEngine = new PolicyEngine();
// Policy management
policyEngine.registerPolicy(policy);
policyEngine.registerPolicies(policies);
policyEngine.evaluate(context);
// Policy templates
PolicyEngine.createOwnershipPolicy(resourceType);
PolicyEngine.createRolePolicy(role, actions, resources);
PolicyEngine.createTimeBasedPolicy(id, name, actions, resources, startTime, endTime);
```
#### `AuthorizationManager`
```typescript
const authManager = new AuthorizationManager({
roleManager,
policyEngine
});
// Authorization checks
const canAccess = await authManager.can(action, resource);
const cannotAccess = await authManager.cannot(action, resource);
// Bulk checks
const permissions = await authManager.getPermissions(subject);
const roles = await authManager.getRoles(subject);
```
---
## ๐ค Contributing
We love contributions! Here's how to get started:
### Quick Setup
```bash
# Clone and setup
git clone https://github.com/saaskit/multitenancy.git
cd multitenancy
npm install
# Run tests
npm test
# Build packages
npm run build
# Try the example
cd examples/express-demo
npm run dev
```
### Contribution Ideas
- ๐ฏ **New adapters** - Add support for more ORMs/databases
- ๐ง **Framework integrations** - Add support for Koa, Hapi, etc.
- ๐ **Examples** - More real-world examples and tutorials
- ๐ **Bug fixes** - Check our [issues](https://github.com/saaskit/multitenancy/issues)
- ๐ **Documentation** - Improve guides and API docs
- โก **Performance** - Optimize hot paths and memory usage
### Development Guidelines
- Write tests for new features
- Follow TypeScript best practices
- Update documentation
- Add examples for new features
- Ensure backward compatibility
---
## ๐ Roadmap
### ๐ฏ Next Release (v2.0)
- [ ] **GraphQL integration** - Schema stitching and resolvers
- [ ] **Admin dashboard** - Web UI for tenant management
- [ ] **Advanced caching** - Redis integration and cache invalidation
- [ ] **Tenant analytics** - Usage metrics and insights
- [ ] **Webhook system** - Event-driven tenant lifecycle
### ๐ Future Releases
- [ ] **Python support** - Django and FastAPI adapters
- [ ] **Multi-region** - Geographical tenant distribution
- [ ] **Tenant migration** - Tools for moving tenants between databases
- [ ] **Advanced RBAC** - Hierarchical roles and dynamic permissions
- [ ] **Compliance tools** - GDPR, SOC2, HIPAA helpers
### ๐ก Community Requests
Vote on features at [our discussions](https://github.com/saaskit/multitenancy/discussions)!
---
## ๐ License
MIT License - see [LICENSE](LICENSE) for details.
**Free for commercial use** โ
**No attribution required** โ
**Modify as needed** โ
---
## ๐ Support & Community
๐ Documentation
docs.saaskit.dev
๐ฌ Discord
Join our community
๐ Issues
Report bugs
๐ก Discussions
Share ideas
### Enterprise Support
Need help with production deployment, custom features, or architecture review?
**๐ง [enterprise@saaskit.dev](mailto:enterprise@saaskit.dev)**
We offer:
- ๐ **Architecture consulting** - Design review and best practices
- โก **Performance optimization** - Scale to millions of tenants
- ๐ **Security audits** - Comprehensive security review
- ๐ **Training** - Team training and workshops
- ๐ **Custom development** - Bespoke features and integrations
---
## โญ Show Your Support
If this toolkit saved you weeks of development time, give us a star! โญ
It helps other developers discover the project and motivates us to keep improving it.
[](https://github.com/saaskit/multitenancy)
---
**Built with โค๏ธ by the SaaSKit team**
*Making multi-tenancy accessible to every developer*