Switch engines
Engine migration
Most of AskDB is engine-agnostic. The dialect adapter is the part that knows your SQL flavor — swap it, and the same schema-author workflow and the same ask() call work against a different engine.
What stays the same
Section titled “What stays the same”- The schema artifact format. Same
schema.json, sametables/*.md, same enrichment workflow. - The
ask()API. Same options, same return shape. - The validator semantics: read-only, single-statement, no system schemas, tenant filter enforcement.
- The privacy model. The model sees schema, not rows, regardless of engine.
What changes
Section titled “What changes”- The engine package you install.
- The
dialectvalue you pass toask(). - Engine-specific SQL features the validator allows (LIMIT vs TOP, parameter syntax, JSON path syntax).
- The driver your app uses to execute (
pg,mysql2,better-sqlite3,mssql). - How the CLI introspects the database (connection-string format and catalog queries differ per engine).
The matrix
Section titled “The matrix”| Engine | Adapter package | dialect value | Suggested driver |
|---|---|---|---|
| PostgreSQL | @askdb/postgres | "postgres" | pg |
| MySQL | @askdb/mysql | "mysql" | mysql2 |
| SQLite | @askdb/sqlite | "sqlite" | better-sqlite3 |
| SQL Server | @askdb/sqlserver | "sqlserver" | mssql |
Step by step
Section titled “Step by step”1. Install the new adapter
Section titled “1. Install the new adapter”npm install @askdb/postgrespnpm add @askdb/postgresyarn add @askdb/postgresIf you plan to run live introspection, also add the driver:
npm install pgpnpm add pgyarn add pgnpm install @askdb/mysqlpnpm add @askdb/mysqlyarn add @askdb/mysqlIf you plan to run live introspection, also add the driver:
npm install mysql2pnpm add mysql2yarn add mysql2npm install @askdb/sqlitepnpm add @askdb/sqliteyarn add @askdb/sqliteIf you plan to run live introspection, also add the driver:
npm install better-sqlite3pnpm add better-sqlite3yarn add better-sqlite3npm install @askdb/sqlserverpnpm add @askdb/sqlserveryarn add @askdb/sqlserverIf you plan to run live introspection, also add the driver:
npm install mssqlpnpm add mssqlyarn add mssqlYou can keep the old adapter installed during a transition. Each adapter is self-contained. Database drivers are optional peers used only by live introspection or by your own execution layer.
2. Introspect the new database (if the schema differs)
Section titled “2. Introspect the new database (if the schema differs)”If you are pointing at a different database instance — one that may have a different set of tables or columns — run introspection against it to regenerate the physical layer of the schema artifact:
npx askdb introspect \ --engine postgres \ --url "postgresql://user:pass@host/dbname" \ --out my-app.schema \ --schema-id my-appnpx askdb introspect \ --engine mysql \ --url "mysql://user:pass@host/dbname" \ --out my-app.schema \ --schema-id my-appFor SQLite, set the file path via introspection.providerConfig.sqlite.file in askdb.config.ts:
export default { introspection: { provider: "sqlite", providerConfig: { sqlite: { file: "./my-database.db" }, }, }, // ...};Then run:
npx askdb introspect --out my-app.schema --schema-id my-appnpx askdb introspect \ --engine sqlserver \ --url "mssql://user:pass@host:1433/dbname" \ --out my-app.schema \ --schema-id my-appSQL Server accepts three URL formats — see SQL Server connection strings below.
The connection string format follows each engine’s convention. The engine itself comes from --engine (or introspection.provider in askdb.config.ts) — the CLI does not infer it from the URL. The artifact records which engine produced it, and askdb ask infers the dialect from the artifact.
For one-off live introspection without installing askdb in the project, include the driver in the same ephemeral command:
pnpm dlx -p askdb -p mysql2 askdb introspect --engine mysql --url "$MYSQL_URL"npx -p askdb -p mssql askdb introspect --engine sqlserver --url "$SQLSERVER_URL"If the logical schema is identical and you are only swapping the adapter package, the existing artifact is already valid — skip this step and go straight to step 3.
3. Update the dialect value in ask() calls
Section titled “3. Update the dialect value in ask() calls”The schema artifact records which engine it was introspected from. The CLI (askdb ask) and askdb.config.ts use that information to infer the dialect automatically — no manual update needed for those entry points.
This holds for Prisma-introspected artifacts too: the connector reads the datasource.provider from your .prisma file (postgresql → postgres, mysql, sqlite, sqlserver) and records it, so askdb ask still knows the dialect without a live database.
When using @askdb/client, the dialect resolves from the schema artifact’s recorded provider automatically — the same inference the CLI uses. No code change needed after a dialect switch if the new artifact declares the correct provider.
To force a specific dialect (for example, if the artifact’s provider doesn’t match your target engine), pass it as a per-call override:
// askdb initialized at startup with createAskDb() — see Embed in a Node app.const { sql } = await askdb.ask(question, { dialect: "mysql", // overrides auto-resolution});All four dialect specs ship inside @askdb/core. The engine packages (@askdb/mysql, @askdb/postgres, …) supply the introspection connector for that engine (and re-export its dialect constant for convenience); you need them to introspect, not to generate.
4. Update your driver
Section titled “4. Update your driver”Swap the driver library for the one that matches the new engine. The execution layer is yours — AskDB doesn’t open the connection — so this is a straight library swap.
import { Pool } from "pg";
const pool = new Pool({ connectionString: process.env.DATABASE_URL, statement_timeout: 5000, max: 10,});
const { rows } = await pool.query(sql);import mysql from "mysql2/promise";
const pool = mysql.createPool({ uri: process.env.MYSQL_URL, connectionLimit: 10,});
const [rows] = await pool.execute(sql);import Database from "better-sqlite3";
const db = new Database(process.env.SQLITE_FILE ?? "./my-database.db");
const rows = db.prepare(sql).all();import mssql from "mssql";
const pool = await mssql.connect(process.env.SQLSERVER_URL);
const result = await pool.request().query(sql);const rows = result.recordset;SQL Server connection strings
Section titled “SQL Server connection strings”databaseUrl in askdb.config.ts and --url on the CLI accept three formats:
| Format | Example |
|---|---|
mssql:// URL | mssql://sa:pass@localhost:1433/MyDb |
Prisma sqlserver:// | sqlserver://localhost:1433;database=MyDb;user=sa;password=pass |
ADO.NET (Key=Value;) | Server=localhost,1433;Database=MyDb;User Id=sa;Password=pass; |
TLS / self-signed certificates
Section titled “TLS / self-signed certificates”SQL Server enables TLS by default. Local or dev instances typically use a self-signed certificate, which causes this error:
self-signed certificate — try running Node.js with --use-system-caFix for development: add the trust option for your format:
# mssql:// URLmssql://sa:pass@localhost:1433/MyDb?trustServerCertificate=true
# Prisma URLsqlserver://localhost:1433;database=MyDb;user=sa;password=pass;trustServerCertificate=true
# ADO.NET — use the camelCase key without spacesServer=localhost,1433;Database=MyDb;User Id=sa;Password=pass;Encrypt=True;TrustServerCertificate=True;Note: If you copy a connection string from VS Code’s mssql extension it will contain
Trust Server Certificate=True(with spaces). AskDB normalises this automatically so it works, but you can also write it without spaces (TrustServerCertificate=True) to match what mssql v12 expects natively.
Fix for production: install a properly signed certificate or install the server’s CA certificate in the Node.js trust store:
NODE_EXTRA_CA_CERTS=/path/to/ca.pem npx askdb introspect# or (Node ≥ 24.3)node --use-system-ca $(which askdb) introspectOn dialect parity
Section titled “On dialect parity”Postgres is the reference dialect — features ship there first and the other engines follow. For most analytical questions, the four dialects generate equivalent SQL; for engine-specific features (JSON operators, window function syntax, regex variations), behavior follows what each engine supports natively.
If you hit a dialect difference that surprises you, that’s a bug — file it on the project issues.
Running multiple engines side by side
Section titled “Running multiple engines side by side”Nothing stops you from querying two schema artifacts against different dialects in the same process. With @askdb/client, pass the schema and dialect as per-call overrides:
// askdb initialized at startup with createAskDb() — see Embed in a Node app.
const fromOrders = await askdb.ask(question, { schema: { path: "./orders.schema" }, dialect: "postgres",});
const fromAnalytics = await askdb.ask(question, { schema: { path: "./analytics.schema" }, dialect: "mysql",});Read next
Section titled “Read next”© 2026 Yahya Gilany

