Skip to main content
Version: 7.0 (next)

Creating Custom Driver

If you want to use a database that is not currently supported, you can implement your own driver. The driver architecture is split into several classes, each handling a different concern.

Architecture Overview

MikroORM drivers are composed of 3 main classes:

  • Platform — describes database features and capabilities
  • Connection — handles the actual database communication
  • Driver — orchestrates the connection and platform to persist changes

For SQL databases, all three have specialized base classes in the @mikro-orm/sql package that handle most of the work, so you only need to fill in the database-specific details.

@mikro-orm/core                    @mikro-orm/sql
┌──────────────────┐ ┌───────────────────────┐
│ Platform │──extends──▸ │ AbstractSqlPlatform │
│ Connection │──extends──▸ │ AbstractSqlConnection │
│ DatabaseDriver │──extends──▸ │ AbstractSqlDriver │
└──────────────────┘ └───────────────────────┘

Creating a SQL Driver

Most custom drivers will target an SQL database. In that case, extend the abstract classes from @mikro-orm/sql. The SQL layer uses Kysely under the hood, so your connection only needs to provide a Kysely dialect for the target database.

Connection

The connection is responsible for establishing the database link and providing a Kysely dialect. Extend AbstractSqlConnection and implement the createKyselyDialect() method:

import { AbstractSqlConnection } from '@mikro-orm/sql';
import type { Dialect, Dictionary } from 'kysely';

export class MyConnection extends AbstractSqlConnection {

createKyselyDialect(overrides: Dictionary): Dialect {
// Return a Kysely dialect for your database.
// `overrides` contains driver options from the MikroORM config.
return new MyKyselyDialect({ ... });
}

}

The base class handles connection lifecycle, transactions, savepoints, query execution, and streaming — all via Kysely.

Platform

The platform describes your database's features. Extend AbstractSqlPlatform and override methods as needed:

import { AbstractSqlPlatform } from '@mikro-orm/sql';

export class MyPlatform extends AbstractSqlPlatform {

// Override methods to describe your database's capabilities, e.g.:
supportsTransactions(): boolean { return true; }
usesReturningStatement(): boolean { return false; }
getDefaultSchemaName(): string | undefined { return undefined; }

}

AbstractSqlPlatform extends the core Platform and provides sensible SQL defaults. Common methods you might override:

MethodPurpose
supportsTransactions()Does the database support transactions?
usesReturningStatement()Support for INSERT ... RETURNING?
supportsSchemas()Support for named schemas (e.g. PostgreSQL)?
getDefaultSchemaName()Default schema name if schemas are supported
quoteIdentifier(id)How to quote identifiers (default: "id")
quoteValue(value)How to quote literal values
getCurrentTimestampSQL(length)SQL expression for current timestamp
getSearchJsonPropertySQL(path, type, aliased)JSON property access syntax
escape(value)Escape a literal value for SQL

For schema generation support, you can also provide a custom SchemaHelper — see the existing drivers for reference (e.g. PostgreSqlSchemaHelper, MySqlSchemaHelper).

Driver

The driver ties everything together. Extend AbstractSqlDriver, provide your connection class and platform, and specify the native dependencies your driver needs:

import { type Configuration, EntityManagerType } from '@mikro-orm/core';
import { AbstractSqlDriver } from '@mikro-orm/sql';
import { MyConnection } from './MyConnection.js';
import { MyPlatform } from './MyPlatform.js';

export class MyDriver extends AbstractSqlDriver<MyConnection> {

constructor(config: Configuration) {
super(config, new MyPlatform(), MyConnection, ['kysely', 'my-native-driver']);
}

}

The last argument to super() is an array of npm package names that must be installed — MikroORM will check for them at startup and provide a helpful error message if any are missing.

AbstractSqlDriver handles all the heavy lifting: find, findOne, nativeInsert, nativeUpdate, nativeDelete, count, query building, joined loading strategy, and more.

Using the custom driver

Provide the driver class to MikroORM via the driver config option:

const orm = await MikroORM.init({
driver: MyDriver,
dbName: 'my-database',
entities: [Author, Book],
});

Creating a Non-SQL Driver

For non-SQL databases (like document stores or key-value databases), extend the core classes from @mikro-orm/core directly. You will need to implement more methods yourself, since there is no SQL query builder to lean on.

Connection

Extend Connection and implement the abstract methods:

import { Connection } from '@mikro-orm/core';

export class MyConnection extends Connection {

async connect(): Promise<void> {
// establish connection to the database
}

async isConnected(): Promise<boolean> {
// return true if connected
}

async checkConnection(): Promise<{ ok: true } | { ok: false; reason: string; error?: Error }> {
// verify the connection is alive
}

async close(force?: boolean): Promise<void> {
// close the connection
}

// Add your own query methods specific to your database.

}

Platform

Extend Platform and override methods to describe your database's capabilities:

import { Platform } from '@mikro-orm/core';

export class MyPlatform extends Platform {

// Override feature flags as needed, e.g.:
usesPivotTable(): boolean { return false; }
supportsTransactions(): boolean { return false; }
getNamingStrategy(): { new (): NamingStrategy } { return MyNamingStrategy; }
normalizePrimaryKey<T = number | string>(data: IPrimaryKey): T { ... }
denormalizePrimaryKey(data: IPrimaryKey): IPrimaryKey { ... }

}

Driver

Extend DatabaseDriver and implement the abstract methods for CRUD operations:

import { DatabaseDriver, type Configuration, EntityManagerType } from '@mikro-orm/core';
import { MyConnection } from './MyConnection.js';
import { MyPlatform } from './MyPlatform.js';

export class MyDriver extends DatabaseDriver<MyConnection> {

protected override readonly connection = new MyConnection(this.config);
protected override readonly platform = new MyPlatform();

constructor(config: Configuration) {
super(config, ['my-native-driver']);
}

// Implement abstract CRUD methods:
async find<T extends object>(/* ... */): Promise<EntityData<T>[]> { ... }
async findOne<T extends object>(/* ... */): Promise<EntityData<T> | null> { ... }
async nativeInsert<T extends object>(/* ... */): Promise<QueryResult<T>> { ... }
async nativeInsertMany<T extends object>(/* ... */): Promise<QueryResult<T>> { ... }
async nativeUpdate<T extends object>(/* ... */): Promise<QueryResult<T>> { ... }
async nativeDelete<T extends object>(/* ... */): Promise<QueryResult<T>> { ... }
async count<T extends object>(/* ... */): Promise<number> { ... }

}

See the MongoDB driver source for a complete real-world example of a non-SQL driver.

Custom SQLite driver

For SQLite-based databases, the @mikro-orm/sql package provides a generic SqliteDriver that works with any SQLite library via Kysely dialects. See the SQLite guide for details.

Reference Implementations

The best way to understand the full picture is to look at the existing driver packages:

  • PostgreSQL (@mikro-orm/postgresql) — full-featured SQL driver with schema support, JSON operators, arrays, and more
  • MySQL (@mikro-orm/mysql) — SQL driver with MySQL-specific platform features
  • SQLite (@mikro-orm/sqlite) — minimal SQL driver, good starting point
  • MongoDB (@mikro-orm/mongodb) — non-SQL driver extending DatabaseDriver directly

All driver packages live in the packages/ directory of the MikroORM repository.