@helix/shared/migrations
Unified migration services for Tier 2 (tenant) and Tier 3 (product) databases.
Overview
Provides migration services that work like Laravel's php artisan tenants:migrate:
- TenantMigrator: Migrate all tenant databases (Tier 2)
- ProductMigrator: Migrate all product databases (Tier 3)
Both support:
- Deployment: Migrate all active databases
- Provisioning: Migrate a single newly created database
Installation
This is an internal library. Import using:
import { TenantMigrator, ProductMigrator } from '@helix/shared/migrations';
Usage
Deployment (All Databases)
# Migrate all tenant databases (Tier 2)
npm run migrate:tenants
# Migrate all product databases (Tier 3)
npm run migrate:products
# Migrate specific product type
npm run migrate:products -- --product=kira
Provisioning (Single Database)
import { TenantMigrator, ProductMigrator } from '@helix/shared/migrations';
// After creating new tenant
const tenant = await tenantService.create(data);
await tenantMigrator.migrateOne(tenant.id);
// After provisioning new product
const product = await productService.provision(tenantId, 'kira');
await productMigrator.migrateOne(product.id);
TenantMigrator (Tier 2)
Migrates tenant shared databases (tenant_{uuid}_shared).
Methods
migrateAll(concurrency?: number)
Migrate all active tenant databases in parallel.
const results = await tenantMigrator.migrateAll(5);
// { success: ['tenant_1', 'tenant_2'], failed: [] }
migrateOne(tenantId: string)
Migrate a single tenant database.
await tenantMigrator.migrateOne('abc-123');
needsMigration(tenantId: string)
Check if tenant database has pending migrations.
const pending = await tenantMigrator.needsMigration('abc-123');
// true if migrations pending
Example: Tenant Provisioning
@Injectable()
export class TenantService {
constructor(
private readonly db: DatabaseService,
private readonly tenantMigrator: TenantMigrator,
) {}
async createTenant(data: CreateTenantDto) {
// 1. Create tenant record
const tenant = await this.db.central().tenant.create({
data: {
name: data.name,
domain: data.domain,
jurisdiction: data.jurisdiction,
workosOrganizationId: data.workosOrgId,
},
});
// 2. Provision Tier 2 database
const databaseName = `tenant_${tenant.id}_shared`;
const dbCreds = await this.createDatabase(databaseName, data.jurisdiction);
// 3. Store database credentials
await this.db.central().tenantDatabase.create({
data: {
tenantId: tenant.id,
databaseHost: dbCreds.host,
databasePort: dbCreds.port,
databaseName: dbCreds.name,
databaseUsername: dbCreds.username,
databasePassword: dbCreds.password, // Encrypted
},
});
// 4. Run migrations on new tenant database
await this.tenantMigrator.migrateOne(tenant.id);
return tenant;
}
}
ProductMigrator (Tier 3)
Migrates product databases (tenant_{uuid}_{product}).
Methods
migrateAll(concurrency?: number)
Migrate all active product databases in parallel.
const results = await productMigrator.migrateAll(5);
// { success: ['product_1', 'product_2'], failed: [] }
migrateOne(tenantProductId: string)
Migrate a single product database.
await productMigrator.migrateOne('product-xyz');
migrateByProduct(productType: string, concurrency?: number)
Migrate all databases for a specific product type.
// Migrate all KIRA databases
const results = await productMigrator.migrateByProduct('kira', 5);
needsMigration(tenantProductId: string)
Check if product database has pending migrations.
const pending = await productMigrator.needsMigration('product-xyz');
Example: Product Provisioning
@Injectable()
export class ProductProvisioningService {
constructor(
private readonly db: DatabaseService,
private readonly productMigrator: ProductMigrator,
) {}
async provisionProduct(tenantId: string, productType: string) {
const tenant = await this.db.central().tenant.findUnique({
where: { id: tenantId },
});
// 1. Create product record
const product = await this.db.central().tenantProduct.create({
data: {
tenantId,
productType,
s3BucketPath: `helix-${tenant.jurisdiction}/${tenantId}/${productType}`,
},
});
// 2. Provision Tier 3 database
const databaseName = `tenant_${tenantId}_${productType}`;
const dbCreds = await this.createDatabase(databaseName, tenant.jurisdiction, productType);
// 3. Store database credentials
await this.db.central().productDatabase.create({
data: {
tenantProductId: product.id,
databaseHost: dbCreds.host,
databasePort: dbCreds.port,
databaseName: dbCreds.name,
databaseUsername: dbCreds.username,
databasePassword: dbCreds.password, // Encrypted
},
});
// 4. Run migrations on new product database
await this.productMigrator.migrateOne(product.id);
return product;
}
}
Deployment Flow
#!/bin/bash
# deploy.sh
echo "Deploying Helix platform..."
# 1. Pull latest code
git pull origin main
# 2. Install dependencies
npm install
# 3. Build all services
nx build --all
# 4. Run migrations (in order)
echo "Running Central DB migrations..."
cd prisma/central && npx prisma migrate deploy && cd ../..
echo "Running Tenant DB migrations..."
npm run migrate:tenants
echo "Running Product DB migrations..."
npm run migrate:products
# 5. Restart services
pm2 restart all
echo "Deployment complete!"
CLI Commands
Add to package.json:
{
"scripts": {
"migrate:central": "cd prisma/central && npx prisma migrate deploy",
"migrate:tenants": "tsx scripts/migrate-tenants.ts",
"migrate:products": "tsx scripts/migrate-products.ts"
}
}
CLI Scripts
// scripts/migrate-tenants.ts
import { NestFactory } from '@nestjs/core';
import { TenantMigrator } from '@helix/shared/migrations';
import { AppModule } from './migration-app.module';
async function migrate() {
const app = await NestFactory.createApplicationContext(AppModule);
const migrator = app.get(TenantMigrator);
const results = await migrator.migrateAll();
console.log(`Success: ${results.success.length}`);
console.log(`Failed: ${results.failed.length}`);
if (results.failed.length > 0) {
process.exit(1);
}
await app.close();
}
migrate();
// scripts/migrate-products.ts
import { NestFactory } from '@nestjs/core';
import { ProductMigrator } from '@helix/shared/migrations';
import { AppModule } from './migration-app.module';
async function migrate() {
const app = await NestFactory.createApplicationContext(AppModule);
const migrator = app.get(ProductMigrator);
const productType = process.argv.find(arg => arg.startsWith('--product='))?.split('=')[1];
const results = productType
? await migrator.migrateByProduct(productType)
: await migrator.migrateAll();
console.log(`Success: ${results.success.length}`);
console.log(`Failed: ${results.failed.length}`);
if (results.failed.length > 0) {
process.exit(1);
}
await app.close();
}
migrate();
Error Handling
// Tenant not found
try {
await tenantMigrator.migrateOne('invalid-id');
} catch (error) {
// Error: Tenant not found: invalid-id
}
// Database not provisioned
try {
await tenantMigrator.migrateOne('tenant-without-db');
} catch (error) {
// Error: Tenant database not provisioned: tenant-without-db
}
// Migration failed
try {
await productMigrator.migrateOne('product-123');
} catch (error) {
// Error: Prisma migration failed: ...
}
Multi-Jurisdiction Support
Migrations respect the tenant's jurisdiction:
// UK Tenant
{
jurisdiction: 'uk',
tenantDatabase: {
host: 'uk-tier2-db.helix.com', // UK database server
// ...
}
}
// EU Tenant
{
jurisdiction: 'eu',
tenantDatabase: {
host: 'eu-tier2-db.helix.com', // EU database server
// ...
}
}
Migrations run against the correct database server based on stored credentials.
Concurrency Control
Both migrators support parallel execution with concurrency limits:
// Migrate 10 tenants at a time
await tenantMigrator.migrateAll(10);
// Migrate 5 products at a time
await productMigrator.migrateAll(5);
Default: 5 concurrent migrations
Why Limit?
- Prevents overwhelming database servers
- Avoids connection pool exhaustion
- Better error tracking
Monitoring
Both migrators provide detailed logging:
[TenantMigrator] Starting migration for all tenant databases...
[TenantMigrator] Found 50 active tenants to migrate
[TenantMigrator] ✓ Migrated tenant: Acme Corp (abc-123)
[TenantMigrator] ✓ Migrated tenant: Widget Inc (def-456)
[TenantMigrator] ✗ Failed to migrate tenant: Test Co (xyz-789)
[TenantMigrator] Migration complete: 49 succeeded, 1 failed
Related Libraries
@helix/shared/database- Database service using these migrations- Central DB client (see Database & Prisma)
- Tenant DB client (see Database & Prisma)
Development
# Build the library
nx build shared-migrations
# Lint the library
nx lint shared-migrations
# Test the library
nx test shared-migrations
License
Proprietary - CleverChain Limited