MySQL
The most popular open-source relational database. Fast reads, wide ecosystem, battle-tested in web applications at every scale.

Through TenantsDB, each tenant gets their own MySQL database with the same schema, deployed from a blueprint. The proxy handles routing, TLS, query logging, and settings enforcement. Your app connects with any standard MySQL driver. No SDK needed.

The proxy supports the MySQL wire protocol including prepared statements (binary protocol) and text protocol queries. Control workspaces give your application a managed backend database with full DDL access. Tenant workspaces track schema changes as blueprints for deployment.

Unlike PostgreSQL where TLS is configured via URL parameter, MySQL TLS is configured differently per driver. Each ORM example below includes the correct TLS setup for that language.


Connect to Control Workspace
Your application's backend database. Users, billing, config. Full DDL and DML access, no blueprints.
Shell
mysql -h mysql.tenantsdb.com -P 3306 \
  -u tdb_2abf90d3 -ptdb_d2bf66ed7898c448 \
  controlplane_workspace --ssl-mode=REQUIRED

Control mode workspaces accept all DDL immediately. No blueprint versioning, no deployment step. Schema changes take effect as soon as you run them. Use this for your application's own tables that are not per-tenant.


Connect to Tenant Workspace
Where you design and iterate on your tenant schema. DDL changes are tracked as versioned blueprints.
Shell
mysql -h mysql.tenantsdb.com -P 3306 \
  -u tdb_2abf90d3 -ptdb_d2bf66ed7898c448 \
  myapp_workspace --ssl-mode=REQUIRED

Every CREATE TABLE, ALTER TABLE, or other DDL statement you run here is captured as a blueprint version. Deploy it to all tenants with tdb deployments create --blueprint myapp --all.


Connect to Tenant Databases
Isolated production databases for your customers. CRUD only. DDL is blocked.
acme
Shell
mysql -h mysql.tenantsdb.com -P 3306 \
  -u tdb_2abf90d3 -ptdb_d2bf66ed7898c448 \
  myapp__acme --ssl-mode=REQUIRED
globex
Shell
mysql -h mysql.tenantsdb.com -P 3306 \
  -u tdb_2abf90d3 -ptdb_d2bf66ed7898c448 \
  myapp__globex --ssl-mode=REQUIRED
Same credentials, different database name. Each tenant's data is fully isolated. DDL statements return a clear error: DDL not allowed on tenant databases - use workspace mode.

Build Schema
Connect to your tenant workspace and create tables. Every DDL change is tracked as a blueprint version.
SQL
CREATE TABLE accounts (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) UNIQUE NOT NULL,
    balance DECIMAL(15,2) DEFAULT 0,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO accounts (name, email, balance) VALUES
  ('Alice', '[email protected]', 1000),
  ('Bob', '[email protected]', 2000);
Only DDL statements (CREATE TABLE, ALTER TABLE, etc.) are tracked as blueprint changes. DML statements (INSERT, UPDATE, DELETE) run in the workspace only and are not deployed to tenants.

You can also import an existing schema from another database or use a template. See tdb workspaces schema --help for all options.


ORM & Drivers
Copy-paste examples for every supported ORM and driver. Each example connects to a tenant database and includes TLS configuration. For workspace or control connections, swap the database name.
Sequelize (Node.js)
JavaScript
const { Sequelize, DataTypes } = require('sequelize');

const sequelize = new Sequelize('myapp__acme', 'tdb_2abf90d3', 'tdb_d2bf66ed7898c448', {
  host: 'mysql.tenantsdb.com',
  port: 3306,
  dialect: 'mysql',
  dialectOptions: { ssl: { rejectUnauthorized: false } }
});

// Define models
const Account = sequelize.define('Account', {
  name: { type: DataTypes.STRING(255), allowNull: false },
  email: { type: DataTypes.STRING(255), allowNull: false, unique: true },
  balance: { type: DataTypes.DECIMAL(15, 2), defaultValue: 0 },
}, { tableName: 'accounts', timestamps: true });

// Query
const accounts = await Account.findAll();
await Account.create({ name: 'Alice', email: '[email protected]', balance: 1000 });
Install
npm install sequelize mysql2
Sequelize uses mysql2 internally. TLS is configured via dialectOptions.ssl.
SQLAlchemy (Python)
Python
import ssl
from sqlalchemy import create_engine, Column, Integer, String, Numeric, DateTime
from sqlalchemy.orm import declarative_base, Session
from datetime import datetime

Base = declarative_base()

class Account(Base):
    __tablename__ = 'accounts'
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(255), nullable=False)
    email = Column(String(255), unique=True, nullable=False)
    balance = Column(Numeric(15, 2), default=0)
    created_at = Column(DateTime, default=datetime.utcnow)

ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE

engine = create_engine(
    "mysql+pymysql://tdb_2abf90d3:[email protected]:3306/myapp__acme",
    connect_args={"ssl": ctx}
)

# Query
with Session(engine) as s:
    accounts = s.query(Account).all()
    s.add(Account(name='Alice', email='[email protected]', balance=1000))
    s.commit()
Install
pip install sqlalchemy pymysql
MySQL TLS with pymysql requires an SSL context passed via connect_args. The URL does not include a TLS parameter.
Go (go-sql-driver)
Go
package main

import (
    "database/sql"
    "fmt"

    _ "github.com/go-sql-driver/mysql"
)

func main() {
    db, err := sql.Open("mysql",
        "tdb_2abf90d3:tdb_d2bf66ed7898c448@tcp(mysql.tenantsdb.com:3306)/myapp__acme?tls=true",
    )
    if err != nil {
        panic(err)
    }
    defer db.Close()

    // Query
    rows, _ := db.Query("SELECT id, name, balance FROM accounts")
    for rows.Next() {
        var id int
        var name string
        var balance float64
        rows.Scan(&id, &name, &balance)
        fmt.Printf("%d: %s ($%.2f)\n", id, name, balance)
    }
}
Install
go get github.com/go-sql-driver/mysql
Go's go-sql-driver is the only MySQL driver where ?tls=true works in the connection string. ORMs like GORM use this driver under the hood.

TLS Configuration
MySQL uses STARTTLS (in-protocol TLS upgrade), which means TLS is configured per driver, not via a URL parameter. Each driver has its own syntax.
Driver / ORMTLS Configuration
mysql CLI --ssl-mode=REQUIRED
Sequelize (mysql2) dialectOptions: { ssl: { rejectUnauthorized: false } }
SQLAlchemy (pymysql) connect_args={"ssl": ctx} with ssl.create_default_context()
Go (go-sql-driver) ?tls=true in connection string
All connections to mysql.tenantsdb.com require TLS. Connections without TLS are rejected with a clear error message.

Proxy Behavior
MySQL-specific details about how the proxy handles your queries.
Wire Protocol

The proxy forwards the MySQL wire protocol and supports both text protocol (COM_QUERY) and binary protocol (COM_STMT_EXECUTE) for prepared statements. All standard MySQL clients and ORMs work through the proxy.

Settings Enforcement

The proxy enforces max_rows_per_query, query_timeout_ms, and max_connections at the proxy level. These are configured per workspace and apply to all tenants using that blueprint.

Bulk Data

For bulk imports, use tdb workspaces import-full which connects directly to the source database, splits data by routing field, and creates tenants automatically. Multi-row INSERT statements work through the proxy for ongoing batch operations.

LOAD DATA LOCAL is not supported through the proxy. Use the import endpoint or INSERT statements instead.
Limits

Statements are limited to 16MB per operation through the proxy due to MySQL's single-packet wire protocol boundary. Operations exceeding this return a clear error message. For large binary data, store files in object storage and keep references in the database.