azuremysql SQL dialect (Azure Database for MySQL flexibleazuremysqlazuremysqlmysql2, so AzureMySQLDialectMySQLDialect and models only authentication, TLS and the| Property | Value |
|---|---|
| Dialect name | azuremysql |
| Extends | MySQLDialect |
| Driver | mysql2 |
| Optional SDK | @azure/identity (Entra tokens) |
| Default port | 3306 |
| Cloud SDK | @azure/identity (Entra only) |
| Local runtime | none — hosted service only |
$ npm install ts-prorm-orm mysql2$ pnpm add ts-prorm-orm mysql2$ yarn add ts-prorm-orm mysql2$ bun add ts-prorm-orm mysql2mysql2 is the same driver the mysql dialect uses. Add @azure/identity only for the token-based logins under Managed and cloud variants.import { Prorm } from 'ts-prorm-orm';
export const db = new Prorm({
dialect: 'azuremysql',
host: 'mydb.mysql.database.azure.com',
database: 'app',
username: 'app',
password: process.env.DB_PASSWORD,
});import { Prorm } from 'ts-prorm-orm';
export const db = new Prorm({
dialect: 'azuremysql',
url: 'azuremysql://app:secret@mydb.mysql.database.azure.com:3306/app',
});host, port, database, username and password before the dialect is built; any of those passed alongside it wins.import { DataTypes } from 'ts-prorm-orm';
const User = db.define('User', {
id: { type: DataTypes.INTEGER(), primaryKey: true, autoIncrement: true },
email: { type: DataTypes.STRING(255), allowNull: false, unique: true },
name: { type: DataTypes.STRING(100) },
});
await db.sync(); // CREATE TABLE — see Generated SQL belowimport { Op } from 'ts-prorm-orm';
await User.create({ email: 'ada@example.com', name: 'Ada' });
const page = await User.findAll({
where: { name: { [Op.like]: 'A%' } },
order: [['id', 'ASC']],
limit: 20,
offset: 40,
});import { Prorm } from 'ts-prorm-orm';
export const db = new Prorm({
dialect: 'azuremysql',
host: 'mydb.mysql.database.azure.com',
database: 'app',
username: 'ana@contoso.com', // the Entra principal
dialectOptions: {
entra: true, // DefaultAzureCredential: env, workload/managed identity, Azure CLI
},
});@azure/identity installed next to the driver.import { Prorm } from 'ts-prorm-orm';
export const db = new Prorm({
dialect: 'azuremysql',
host: 'mydb.mysql.database.azure.com',
database: 'app',
username: 'ana@contoso.com',
dialectOptions: {
entra: { credential: { type: 'managed-identity', clientId: process.env.AZURE_CLIENT_ID } },
},
});@azure/identity installed next to the driver.import { Prorm } from 'ts-prorm-orm';
export const db = new Prorm({
dialect: 'azuremysql',
host: 'mydb.mysql.database.azure.com',
database: 'app',
username: 'ana@contoso.com',
dialectOptions: {
entra: {
credential: {
type: 'client-secret',
tenantId: process.env.AZURE_TENANT_ID!,
clientId: process.env.AZURE_CLIENT_ID!,
clientSecret: process.env.AZURE_CLIENT_SECRET!,
},
},
},
});@azure/identity installed next to the driver.import { Prorm } from 'ts-prorm-orm';
export const db = new Prorm({
dialect: 'azuremysql',
host: 'mydb.mysql.database.azure.com',
database: 'app',
username: 'app',
password: process.env.DB_PASSWORD,
});query(), captured with the driver stubbed out. The model layer inlines the values.db.sync() for the model in the Quick start:CREATE TABLEawait User.create({ email: 'ada@example.com', name: 'Ada' });
await User.update({ name: 'Grace' }, { where: { email: 'ada@example.com' } });
await User.destroy({ where: { id: 1 } });await User.upsert({ id: 1, email: 'ada@example.com', name: 'Ada Lovelace' });upsert() also reads the row by primary key before and after the write, to return the instance.await User.findAll({
where: { name: { [Op.like]: 'A%' } },
order: [['id', 'ASC']],
limit: 20,
offset: 40,
});[!NOTE] No JSON operator in the WHERE builder DataTypes.JSON()maps toJSON, but this dialect's WHERE builder refusesOp.containson it: Cannot render WHERE condition at meta.contains' is not implemented by its WHERE builder. The statement was not sent, because running it without the condition would match rows it should not.
await db.transaction(async (t) => {
await User.create({ email: 'grace@example.com', name: 'Grace' }, { transaction: t });
// A nested transaction is a SAVEPOINT; its failure undoes only its own work.
await db
.transaction(async (sp) => {
await User.destroy({ where: { id: 1 }, transaction: sp });
throw new Error('keep the row');
}, { transaction: t })
.catch(() => {});
});SKIP LOCKED)lock with skipLocked inside a transaction:await db.transaction(async (t) => {
await User.findAll({ where: { name: 'Ada' }, lock: { level: 'UPDATE', skipLocked: true }, transaction: t });
});STRING → VARCHAR(255), auto-increment INTEGER → INT, BOOLEAN → TINYINT(1), DATE → DATETIME, UUID → CHAR(36), JSON → JSON. The type comparison lines the major engines up side by side.import { Prorm } from 'ts-prorm-orm';
new Prorm({
dialect: 'azuremysql',
host: 'mydb.mysql.database.azure.com',
database: 'app',
username: 'ana@contoso.com', // or the Entra group name
dialectOptions: {
entra: true, // or { credential: {...} }, or tokenProvider
},
});
new Prorm({
dialect: 'azuremysql',
host,
database,
username: 'admin',
password: '…',
});https://ossrdbms-aad.database.windows.net/.default) as a clear-text passwordmysql_clear_password. With entra, theentraTokenProvider({ target: 'mysql-flexible' }) and itsresolvePoolConfig override spreads mysqlAuth() intomysql_clear_password auth plugin that fetches apassword is removed andenableCleartextPlugin is not set. Microsoft will accept onlyossrdbms-audience tokens from January 2027.require_secure_transport is ON by default. When ssl is not given{ rejectUnauthorized: true }). With Entra, construction throws if ssl isusername is missing, or if password is also set; theAzureMySQLDialect extends MySQLDialect (mysql) andObject.getOwnPropertyNames(AzureMySQLDialect.prototype),| Area | Overridden |
|---|---|
| Connection and execution | query, resolvePoolConfig |
| DDL and schema | buildCreateAadUserSql, buildGrantQuery, createPartitionedTable, createTable |
| Introspection | assertEngine, assertSupportedStatement |
| Call | Why |
|---|---|
createTable / createPartitionedTable with engine MyISAM, BLACKHOLE, ARCHIVE, FEDERATED | Only InnoDB and MEMORY are supported |
buildGrantQuery with SUPER, FILE, CREATE TABLESPACE, SHUTDOWN | Restricted static privileges |
SELECT … INTO OUTFILE through query() | No file-system access |
LOAD DATA INFILE without LOCAL through query() | Only LOAD DATA LOCAL INFILE |
query() checks ignore literals and comments and can be turned off withstatementGuard: false.d.buildCreateAadUserSql('user1@yourtenant.onmicrosoft.com');
// CREATE AADUSER 'user1@yourtenant.onmicrosoft.com'
d.buildCreateAadUserSql('userWithLongName@yourtenant.onmicrosoft.com', 'shortName');
// CREATE AADUSER '…' AS 'shortName' — required over 32 characters
d.buildCreateAadUserSql('Prod_DB_Readonly'); // a groupGRANT privileges separately.mysql2; nothing has run against a live server.DEFINER clauses (which need SUPER) and writes to the read-only mysqlmysql_clear_password for an Entra usercapabilitiesOf() (src/transaction-support.ts),whereSupportOf() (src/query-builders/expression-render.ts),sqlObjectCapabilitiesOf() (src/sql-objects/capabilities.ts) and the| Capability | azuremysql |
|---|---|
| Transactions atomic | yes |
| Savepoints | yes |
| Isolation | up to SERIALIZABLE |
| Transaction modes | read-only yes; deferrable no; constraint deferral no |
| RETURNING | not emitted; returning is accepted and ignored |
| Upsert | INSERT … ON DUPLICATE KEY UPDATE |
| JSON column | JSON (JSONB → JSON) |
| Vector column | VECTOR(3) |
| Array column | VARCHAR(255) |
| WHERE builder | 28 operator keys plus the $json: prefix; $and/$or/$not; $exists: no; $in subquery: no; expression operands: no |
| SQL objects | family mysql: triggers yes; procedures yes; functions multiStatement; materialized views emulated (procedure); sequences no; enums inline; RLS no |
| Streaming | queryStream() present; LISTEN absent; createExtension() present |