Does MySQL Work With Drizzle ORM?
MySQL and Drizzle ORM work excellently together with first-class support and a smooth developer experience.
Quick Facts
How MySQL Works With Drizzle ORM
Drizzle ORM provides native MySQL support through the `mysql2` driver, making it a natural fit for MySQL databases. The integration is seamless—you define your schema using Drizzle's TypeScript-first approach, then generate and run migrations automatically. The developer experience is exceptional because Drizzle's query builder feels like writing SQL directly, avoiding the abstraction bloat of traditional ORMs while maintaining type safety throughout your entire data layer.
The architecture works by leveraging the lightweight `mysql2/promise` library under the hood, giving you async/await patterns with full TypeScript inference. You get automatic type generation from your schema, which means your database structure is your single source of truth. Migrations are SQL-based and generated from your schema changes, keeping you in control of what actually runs on your database.
For production use, Drizzle handles connection pooling gracefully and supports both single connections and connection pools depending on your deployment model. The query builder integrates perfectly with frameworks like Next.js, Express, and Fastify, making it ideal for modern Node.js applications that need reliable database access without heavyweight ORM overhead.
Best Use Cases
Quick Setup
npm install drizzle-orm mysql2 dotenvimport { drizzle } from 'drizzle-orm/mysql2';
import mysql from 'mysql2/promise';
import { mysqlTable, varchar, int } from 'drizzle-orm/mysql-core';
const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: varchar({ length: 255 }).notNull(),
email: varchar({ length: 255 }).unique(),
});
const poolConnection = await mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
});
const db = drizzle(poolConnection);
// Query example
const result = await db.select().from(users).where(eq(users.email, 'test@example.com'));
console.log(result);Known Issues & Gotchas
MySQL JSON column types require explicit casting in some query scenarios
Fix: Use Drizzle's `json()` column type and cast explicitly when filtering JSON fields using `.cast()` or raw SQL
MySQL doesn't support partial indexes, limiting some advanced query optimization patterns
Fix: Design indexes carefully; use filtering in application code for partial index logic rather than relying on database-level constraints
Drizzle migrations auto-increment IDs can conflict if you have existing auto-increment columns
Fix: Review generated migrations before applying; manually adjust auto-increment sequences if needed in legacy databases
Transaction isolation level differences between MySQL versions can cause unexpected behavior
Fix: Explicitly set isolation levels in transactions if relying on specific serialization behavior
Alternatives
- •Sequelize + MySQL: Full-featured ORM with migrations, but heavier abstraction layer
- •TypeORM + MySQL: Enterprise-grade ORM with decorators, more opinionated than Drizzle
- •Prisma + MySQL: Modern ORM with visual schema editor, but less SQL-like query syntax
Resources
Related Compatibility Guides
Explore more compatibility guides