Last Updated: 3/20/2026
Dialects
A dialect is the bridge between Kysely and your database engine. It encapsulates all database-specific behavior: how to connect, how to compile queries into SQL, and how to handle database-specific features like RETURNING clauses or transactional DDL.
What Is a Dialect?
The Dialect interface (defined in src/dialect/dialect.ts) requires four factory methods:
createDriver()— Returns aDriverthat manages the connection pool and executes compiled queries against the database.createQueryCompiler()— Returns aQueryCompilerthat transforms Kysely’s internal operation node tree into a SQL string. Different databases use different SQL syntax (e.g., PostgreSQL uses$1placeholders, MySQL uses?), so each dialect has its own compiler.createAdapter()— Returns aDialectAdapterthat handles dialect-specific behaviors like whether the database supportsRETURNING, transactional DDL, orCREATE IF NOT EXISTS.createIntrospector()— Returns aDatabaseIntrospectorfor reading schema metadata (table names, column names, etc.).
When you instantiate new Kysely({ dialect }), the constructor calls these factory methods to build the execution pipeline.
Built-In Dialects
Kysely ships with four built-in dialects:
PostgresDialect
Uses the pg library. Supports RETURNING, transactional DDL, and advisory locks for migrations.
import { Kysely, PostgresDialect } from 'kysely'
import { Pool } from 'pg'
const db = new Kysely<Database>({
dialect: new PostgresDialect({
pool: new Pool({
host: 'localhost',
database: 'my_db',
user: 'postgres',
password: 'secret',
max: 10,
})
})
})You can also pass a function that returns a pool, which defers pool creation until the first query:
new PostgresDialect({
pool: async () => new Pool({
host: 'localhost',
database: 'my_db',
})
})The PostgresAdapter (see src/dialect/postgres/postgres-adapter.ts) sets supportsTransactionalDdl and supportsReturning to true. It uses PostgreSQL’s pg_advisory_xact_lock function to acquire migration locks, which are automatically released at the end of the transaction.
MysqlDialect
Uses the mysql2 library. Does not support RETURNING (uses insertId instead) and does not support transactional DDL.
import { Kysely, MysqlDialect } from 'kysely'
import { createPool } from 'mysql2'
const db = new Kysely<Database>({
dialect: new MysqlDialect({
pool: createPool({
host: 'localhost',
database: 'my_db',
user: 'root',
password: 'secret',
})
})
})Like PostgreSQL, you can pass a pool factory function. The MysqlAdapter (see src/dialect/mysql/mysql-adapter.ts) sets supportsReturning to false and supportsTransactionalDdl to false. It uses MySQL’s GET_LOCK function for migration locking.
MssqlDialect
Uses the tedious library. Supports OUTPUT clauses (similar to RETURNING) and transactional DDL.
import { Kysely, MssqlDialect } from 'kysely'
import * as Tedious from 'tedious'
import * as Tarn from 'tarn'
const db = new Kysely<Database>({
dialect: new MssqlDialect({
tarn: {
...Tarn,
options: {
min: 0,
max: 10,
},
},
tedious: {
...Tedious,
connectionFactory: () => new Tedious.Connection({
server: 'localhost',
authentication: {
type: 'default',
options: {
userName: 'sa',
password: 'secret',
},
},
}),
},
})
})The MssqlAdapter (see src/dialect/mssql/mssql-adapter.ts) sets supportsOutput to true and supportsTransactionalDdl to true.
SqliteDialect
Uses the better-sqlite3 library. Supports RETURNING and transactional DDL.
import { Kysely, SqliteDialect } from 'kysely'
import Database from 'better-sqlite3'
const db = new Kysely<Database>({
dialect: new SqliteDialect({
database: new Database('my.db')
})
})You can also pass an async factory:
new SqliteDialect({
database: async () => new Database('my.db')
})The SqliteAdapter (see src/dialect/sqlite/sqlite-adapter.ts) sets supportsReturning and supportsTransactionalDdl to true.
DialectAdapter
The DialectAdapter interface (defined in src/dialect/dialect-adapter.ts) exposes several boolean flags that control Kysely’s behavior:
supportsReturning— Whether the database supportsRETURNINGclauses inINSERT,UPDATE, andDELETEstatements. PostgreSQL and SQLite support this; MySQL does not.supportsOutput— Whether the database supportsOUTPUTclauses (SQL Server).supportsTransactionalDdl— Whether the database supports running DDL statements (likeCREATE TABLE) inside a transaction. PostgreSQL, SQLite, and SQL Server support this; MySQL does not. Whentrue, Kysely runs migrations inside a transaction.supportsCreateIfNotExists— Whether the database supportsIF NOT EXISTSinCREATE TABLEand similar statements.
The adapter also defines acquireMigrationLock and releaseMigrationLock methods, which Kysely calls to ensure only one migration runs at a time. Each dialect implements this differently: PostgreSQL uses advisory locks, MySQL uses GET_LOCK, and SQLite uses a row lock on the migration lock table.
Community Dialects
The Kysely ecosystem includes community-maintained dialects for databases not covered by the built-in set:
- kysely-d1 — Cloudflare D1 (SQLite in Workers)
- kysely-planetscale — PlanetScale serverless driver
- kysely-libsql — libSQL (Turso)
- kysely-bun-sqlite — Bun’s native SQLite
- kysely-deno-sqlite — Deno’s SQLite
- kysely-singlestore — SingleStore
Each dialect wraps a different database driver and implements the Dialect interface. You use them the same way as the built-in dialects: pass an instance to the dialect option when creating a Kysely instance.
When to Write a Custom Dialect
You need a custom dialect when:
- You want to use a database engine not covered by the built-in or community dialects.
- You need to customize query compilation (e.g., to support a non-standard SQL variant).
- You need to integrate a custom connection pool or driver.
To write a custom dialect, implement the Dialect interface and provide implementations for createDriver, createQueryCompiler, createAdapter, and createIntrospector. Study the built-in dialects in src/dialect/*/ for reference.
What’s Next
- Plugins — Learn how to transform queries and results with the plugin system.
- Schema Builder — Use
db.schemato create and alter tables, indexes, and views. - Generating Types — Automatically generate your
Databaseinterface from your schema.