Drizzle
Drizzle ORM integration for PostgreSQL, MySQL, and SQLite.
@miiajs/drizzle integrates Drizzle ORM with MiiaJS, providing connection management, retry logic, and DI-based access to the database.
Installation
Install the core package and a database driver:
npm install @miiajs/drizzle drizzle-orm postgres
npm install @miiajs/drizzle drizzle-orm mysql2
npm install @miiajs/drizzle drizzle-orm better-sqlite3
Setup
Configure the module
import { Module } from '@miiajs/core'
import { DrizzleModule } from '@miiajs/drizzle'
@Module({
imports: [
DrizzleModule.configure({
dialect: 'postgres',
connection: { url: 'postgres://localhost:5432/mydb' },
}),
],
})
class AppModule {}
With ConfigService (from @miiajs/config):
import { ConfigService } from '@miiajs/config'
DrizzleModule.configure((resolve) => {
const config = resolve(ConfigService)
return {
dialect: 'postgres',
connection: {
url: config.getOrThrow('DATABASE_URL'),
retry: { attempts: 10, delay: 10_000 },
},
}
})
Configuration options
interface DrizzleModuleOptions {
dialect: 'postgres' | 'mysql' | 'sqlite'
connection: {
url: string
retry?: {
attempts?: number // Default: 10
delay?: number // Default: 10_000 ms
}
}
schema?: Record<string, unknown>
casing?: 'snake_case' | 'camelCase'
}
Define schemas
Use standard Drizzle table definitions:
import { pgTable, serial, varchar, timestamp } from 'drizzle-orm/pg-core'
import { sql } from 'drizzle-orm'
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: varchar('name', { length: 255 }).notNull(),
email: varchar('email', { length: 255 }).notNull().unique(),
role: varchar('role', { length: 50 }).notNull().default('user'),
createdAt: timestamp('created_at').default(sql`now()`),
})
Register schemas
In configure
DrizzleModule.configure({
dialect: 'postgres',
connection: { url: DATABASE_URL },
schema: { users, posts },
})
Per feature module
@Module({
imports: [DrizzleModule.register({ users })],
controllers: [UserController],
providers: [UserService],
})
class UserModule {}
@Module({
imports: [DrizzleModule.register({ posts })],
controllers: [PostController],
providers: [PostService],
})
class PostModule {}
Schemas from multiple register() calls are merged automatically.
Use in services
Use injectDrizzle() or inject('DRIZZLE') to access the database directly:
import { Injectable } from '@miiajs/core'
import { injectDrizzle } from '@miiajs/drizzle'
import { eq } from 'drizzle-orm'
import { users } from './user.schema.js'
@Injectable()
class UserService {
private db = injectDrizzle()
async findAll() {
return this.db.select().from(users)
}
async findById(id: number) {
const [user] = await this.db
.select()
.from(users)
.where(eq(users.id, id))
return user ?? null
}
async create(data: { name: string; email: string }) {
const [user] = await this.db
.insert(users)
.values(data)
.returning()
return user
}
async update(id: number, data: Partial<{ name: string; email: string }>) {
const [user] = await this.db
.update(users)
.set(data)
.where(eq(users.id, id))
.returning()
return user
}
async delete(id: number) {
await this.db.delete(users).where(eq(users.id, id))
}
}
Decorator style:
import { Injectable } from '@miiajs/core'
import { InjectDrizzle } from '@miiajs/drizzle'
@Injectable()
class UserService {
@InjectDrizzle() private db!: any
}
Relational queries
When schemas include relations, use db.query for the relational query builder:
import { relations } from 'drizzle-orm'
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}))
// Register both tables and relations
DrizzleModule.register({ users, posts, usersRelations, postsRelations })
// Query with eager loading
const usersWithPosts = await this.db.query.users.findMany({
with: { posts: true },
})
Multiple connections
Use named connections for multi-database setups:
// Root module
@Module({
imports: [
DrizzleModule.configure({
dialect: 'postgres',
connection: { url: MAIN_DB },
}),
DrizzleModule.configure({
dialect: 'postgres',
connection: { url: ANALYTICS_DB },
}, 'analytics'),
UserModule,
AnalyticsModule,
],
})
class AppModule {}
Register schemas per connection:
// UserModule
DrizzleModule.register({ users }) // default connection
// AnalyticsModule
DrizzleModule.register({ events }, 'analytics') // named connection
Inject specific connections:
@Injectable()
class AnalyticsService {
private db = injectDrizzle('analytics')
async getEvents() {
return this.db.select().from(events)
}
}
Supported databases
| Dialect | Driver | Version |
|---|---|---|
postgres | postgres | >= 3.0.0 |
mysql | mysql2 | >= 3.0.0 |
sqlite | better-sqlite3 | >= 11.0.0 |
All drivers are optional peer dependencies — install only what you need.
Connection lifecycle
- onInit — establishes connection with automatic retry on transient errors (
ECONNREFUSED,ECONNRESET,ETIMEDOUT,ENOTFOUND,EAI_AGAIN) - onDestroy — closes the connection gracefully
Exports
import { DrizzleModule, injectDrizzle, InjectDrizzle, getDrizzleToken } from '@miiajs/drizzle'
import type { Dialect, DrizzleModuleOptions } from '@miiajs/drizzle'