Stratum + MySQL
The world's most popular open-source relational database
MySQL is the default database for millions of applications, from WordPress to Laravel to legacy enterprise systems. @stratum-hq/mysql brings Stratum's tenant isolation to MySQL with three strategies: shared table (WHERE tenant_id filtering), table-per-tenant (separate tables per tenant), and database-per-tenant (full database isolation). The control plane (tenant hierarchy, config inheritance, audit log) remains in PostgreSQL via @stratum-hq/lib. MySQL carries your application data, scoped per tenant through structured query methods or ORM integrations.
Step-by-step setup: MySQL guide in the Stratum docs.
Feature comparison
CapabilityMySQLStratum
Isolation strategiesManual WHERE tenant_id on every query3 strategies: shared table, table-per-tenant, database-per-tenant
Tenant scopingApplication-level onlyStructured methods auto-inject tenant_id into all queries
View-based isolationManual CREATE VIEW with session variablesBuilt-in createTenantView() with session variable management
Config inheritanceNot availableBuilt-in via PostgreSQL control plane
GDPR purgeBuild it yourselfpurgeTenantData() for all 3 strategies
ORM supportTypeORM, Knex, Sequelize (manual tenant logic)TypeORM subscriber, Knex helper, Sequelize adapter included
Control planeNonePostgreSQL via @stratum-hq/lib (tenant hierarchy, config, audit)
Security enforcementApplication-level (no RLS in MySQL)Application-level for shared/table; DB-level for database-per-tenant
Getting started
mysql-with-stratum.ts
import mysql from "mysql2/promise"; import { Stratum } from "@stratum-hq/lib"; import { MysqlSharedAdapter } from "@stratum-hq/mysql"; // Control plane: tenant hierarchy lives in PostgreSQL const stratum = new Stratum({ pool }); await stratum.initialize(); // MySQL adapter for application data isolation const mysqlPool = mysql.createPool(process.env.MYSQL_URL); const adapter = new MysqlSharedAdapter({ pool: mysqlPool, databaseName: "myapp", }); // Tenant created in PG control plane const tenant = await stratum.createTenant({ name: "Acme", slug: "acme" }); // Scoped queries auto-inject tenant_id const users = await adapter.scopedSelect(tenant.id, "users"); // only returns this tenant's rows
Things to know
- MySQL has no Row-Level Security. Isolation is enforced at the application layer via structured query methods. Use database-per-tenant for sensitive data requiring physical separation.
- MySQL Views referencing session variables (@stratum_tenant_id) may not use indexes efficiently. Benchmark with large tables. The shared-table adapter's structured methods are the high-performance path.
- The control plane (tenant hierarchy, config, audit log) always requires PostgreSQL via @stratum-hq/lib. MySQL carries application data only.
- Database-per-tenant requires a connection pool per tenant. Configure maxPools to avoid exhausting MySQL connections. Idle pools are automatically closed after 60 seconds.
- The TypeORM subscriber handles writes only (auto-injects tenant_id on insert). Use the shared-table adapter for tenant-scoped reads.
Start building with MySQL + Stratum
npm install @stratum-hq/lib pg