azuresql SQL dialect (Azure SQL Database).azuresqlazuresql<server>.database.windows.net). Clients use TDS on 1433 through the samemssql driver, so AzureSQLDialect extends MSSQLDialect and models only| Property | Value |
|---|---|
| Dialect name | azuresql |
| Extends | MSSQLDialect |
| Driver | mssql (tedious) |
| Optional SDK | @azure/identity (Entra tokens) |
| Default port | 1433 |
| Cloud SDK | @azure/identity (Entra only) |
| Local runtime | none — hosted service only |
$ npm install ts-prorm-orm mssql$ pnpm add ts-prorm-orm mssql$ yarn add ts-prorm-orm mssql$ bun add ts-prorm-orm mssqlmssql is the same driver the mssql 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: 'azuresql',
host: 'myserver.database.windows.net',
database: 'appdb',
username: 'app',
password: process.env.DB_PASSWORD,
});import { Prorm } from 'ts-prorm-orm';
export const db = new Prorm({
dialect: 'azuresql',
url: 'azuresql://app:secret@myserver.database.windows.net:1433/appdb',
});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: 'azuresql',
host: 'myserver.database.windows.net',
database: 'appdb',
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: 'azuresql',
host: 'myserver.database.windows.net',
database: 'appdb',
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: 'azuresql',
host: 'myserver.database.windows.net',
database: 'appdb',
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: 'azuresql',
host: 'myserver.database.windows.net',
database: 'appdb',
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' });MERGE statementupsert() 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,
});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(() => {});
});READPAST)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 → NVARCHAR(255), auto-increment INTEGER → INT, BOOLEAN → BIT, DATE → DATETIME2, UUID → UNIQUEIDENTIFIER, JSON → NVARCHAR(MAX). The type comparison lines the major engines up side by side.import { Prorm } from 'ts-prorm-orm';
// Microsoft Entra ID (DefaultAzureCredential: env, workload/managed identity, Azure CLI)
const dialect = new Prorm({
dialect: 'azuresql',
host: 'myserver.database.windows.net',
database: 'appdb',
dialectOptions: {
entra: true,
},
});
// A specific credential, or your own TokenCredential (no SDK loaded)
new Prorm({
dialect: 'azuresql',
host,
database,
dialectOptions: {
entra: { credential: { type: 'managed-identity', clientId: '…' } },
},
});
// Any token provider (vault, sidecar)
new Prorm({
dialect: 'azuresql',
host,
database,
dialectOptions: {
tokenProvider,
},
});
// tedious's own Entra flows, passed through unchanged
new Prorm({
dialect: 'azuresql',
host,
database,
dialectOptions: {
authentication: { type: 'azure-active-directory-msi-app-service' },
},
});
// SQL authentication still works
new Prorm({
dialect: 'azuresql',
host,
database,
username: 'app',
password: '…',
});| Option | Effect |
|---|---|
entra | true or EntraTokenOptions minus target. Builds entraTokenProvider({ target: 'azure-sql' }) — scope https://database.windows.net/.default. |
tokenProvider | Any TokenProvider. |
authentication | A tedious-native block, passed through. |
statementGuard | Default true; false turns off the statement checks below. |
encrypt, trustServerCertificate | Default true / false. The SQL Server base defaults trustServerCertificate: true; this dialect does not. |
retry | Merged over the Azure defaults (see below). |
entra, tokenProvider, authentication. With a token, theresolvePoolConfig hook: user/password aretediousAuth() suppliesauthentication (the current token) and beforeConnect (which refreshes itprime() is awaited on every connect.encrypt is false or trustServerCertificate is true.AzureSQLDialect extends MSSQLDialect (mssql) andObject.getOwnPropertyNames(AzureSQLDialect.prototype),| Area | Overridden |
|---|---|
| Connection and execution | connect, query, resolvePoolConfig |
| DML builders | buildDeleteDatabaseFirewallRuleSql, buildSetDatabaseFirewallRuleSql, buildVectorDistanceSql |
| DDL and schema | buildAlterUserQuery, buildCreateUserQuery, buildDropUserQuery |
| Introspection | assertSupportedStatement |
| Other | nLiteral |
Database '…' on server '…' is not currently available); theconnect() already retries through withRetry, matchingAzureSQLDialect extends that loop through itsretry options rather than adding a second one — AZURE_SQL_RETRY_DEFAULTS:| Setting | Value |
|---|---|
max | 5 (≈ 2 minutes of waiting in all) |
timeout / backoff | 5000 ms, exponential |
backoffMultiplier / backoffMax | 2 / 60000 ms |
match | the base patterns + AZURE_SQL_TRANSIENT_ERROR_PATTERNS |
AZURE_SQL_TRANSIENT_ERROR_PATTERNS holds the message text of 40613, 40197,query() (and therefore every inherited method) checks each statement first,| Statement | Managed alternative named in the error |
|---|---|
USE <db> (not OPTION (USE HINT …)) | A new AzureSQLDialect with database set |
otherdb.schema.object, otherdb..object after FROM/JOIN/INTO/UPDATE/MERGE/TABLE/EXEC | A separate connection; Managed Instance for cross-database queries. tempdb and the current database are allowed. |
| Four-part (linked server) names | Query that server over its own connection |
SQL Agent: msdb sp_add_job, sp_start_job, schedules, alerts, operators | Elastic jobs or Azure Automation |
BACKUP DATABASE/LOG, RESTORE DATABASE/LOG | Automatic backups, point-in-time restore, LTR, copy / BACPAC |
assertSupportedStatement(sql), findMultipartNames(sql)stripTsqlLiteralsAndComments(sql) are exported for direct use.master, so the SQL ServerCREATE LOGIN … ; CREATE USER … FOR LOGIN pair is replaced by contained users:d.buildCreateUserQuery('app', { password: 's3cret' });
// CREATE USER [app] WITH PASSWORD = N's3cret'
d.buildCreateUserQuery('ana@contoso.com', { externalProvider: true });
// CREATE USER [ana@contoso.com] FROM EXTERNAL PROVIDER
d.buildCreateUserQuery('app-mi', { externalProvider: true, objectId: '…' });
// CREATE USER [app-mi] FROM EXTERNAL PROVIDER WITH OBJECT_ID = N'…'
d.buildAlterUserQuery('app', { password: 'new' }); // ALTER USER [app] WITH PASSWORD = N'new'
d.buildDropUserQuery('app', { ifExists: true }); // DROP USER IF EXISTS [app]ifNotExists guards with sys.database_principals. Locking a user is refusedDENY CONNECT).| Builder | SQL |
|---|---|
buildSetDatabaseFirewallRuleSql(name, start, end) | EXECUTE sp_set_database_firewall_rule @name = N'…', … (IPv4 checked) |
buildDeleteDatabaseFirewallRuleSql(name) | EXECUTE sp_delete_database_firewall_rule @name = N'…' |
buildVectorDistanceSql(metric, a, b) | VECTOR_DISTANCE('cosine' | 'euclidean' | 'dot', a, b) |
json type (generally available) and thevector(n) type (up to 1998 dimensions, float32; float16 is preview);mssql and the token provider.withRetry is private anderror.message. A localized or reworded server message wouldCannot open database), which Microsoft also lists asconnect() is retried. A 40613 or 40197 in the middle of a sessionFROM a, b list, or built dynamically inside EXEC('…'),tediousTokenCredentialAuth() (tedious ≥ 18) is not used, since mssql 10capabilitiesOf() (src/transaction-support.ts),whereSupportOf() (src/query-builders/expression-render.ts),sqlObjectCapabilitiesOf() (src/sql-objects/capabilities.ts) and the| Capability | azuresql |
|---|---|
| Transactions atomic | yes |
| Savepoints | yes (no explicit RELEASE) |
| Isolation | up to SERIALIZABLE; native levels SNAPSHOT |
| Transaction modes | read-only no; deferrable no; constraint deferral no |
| RETURNING | OUTPUT INSERTED.… on INSERT |
| Upsert | MERGE INTO … WHEN MATCHED / WHEN NOT MATCHED |
| JSON column | NVARCHAR(MAX) (JSONB → NVARCHAR(MAX)) |
| Vector column | VECTOR(3) |
| Array column | NVARCHAR(255) |
| WHERE builder | 18 operator keys; $and/$or/$not; $exists: no; $in subquery: no; expression operands: no |
| SQL objects | family mssql: triggers yes; procedures yes; functions multiStatement; materialized views emulated (indexedView); sequences yes; enums check; RLS no |
| Streaming | queryStream() present; LISTEN absent; createExtension() present |