@ereo/db-drizzle
Drizzle ORM adapter for EreoJS. Supports multiple database drivers including PostgreSQL, SQLite, MySQL (via PlanetScale), and edge-compatible options.
Installation
bun add @ereo/db-drizzle
# Install driver based on your database:
bun add drizzle-orm postgres # For PostgreSQL
bun add drizzle-orm @libsql/client # For Turso/LibSQLSupported Drivers
| Driver | Database | Edge Compatible | Best For |
|---|---|---|---|
postgres-js | PostgreSQL | No | Traditional servers, Bun/Node.js |
neon-http | PostgreSQL (Neon) | Yes | Edge deployments, serverless |
neon-websocket | PostgreSQL (Neon) | Yes | Real-time applications |
planetscale | MySQL (PlanetScale) | Yes | Edge deployments, MySQL compatibility |
libsql | SQLite (Turso) | Yes | Edge deployments, distributed SQLite |
bun-sqlite | SQLite | No | Bun runtime, local development |
better-sqlite3 | SQLite | No | Node.js, high-performance SQLite |
d1 | SQLite (Cloudflare) | Yes | Cloudflare Workers |
Quick Start
PostgreSQL
bun add drizzle-orm postgres// ereo.config.ts
import { defineConfig } from '@ereo/core';
import { createDatabasePlugin } from '@ereo/db';
import { createDrizzleAdapter, definePostgresConfig } from '@ereo/db-drizzle';
import * as schema from './schema';
const adapter = createDrizzleAdapter(
definePostgresConfig({
url: process.env.DATABASE_URL!,
schema,
})
);
export default defineConfig({
plugins: [createDatabasePlugin(adapter)],
});Edge with Neon
bun add drizzle-orm @neondatabase/serverlessimport { createDrizzleAdapter, defineEdgeConfig } from '@ereo/db-drizzle';
const adapter = createDrizzleAdapter(
defineEdgeConfig({
driver: 'neon-http',
url: process.env.DATABASE_URL!,
schema,
})
);Adapter Factory
createDrizzleAdapter
Creates a Drizzle database adapter implementing the DatabaseAdapter interface.
function createDrizzleAdapter<TSchema = unknown>(
config: DrizzleConfig
): DatabaseAdapter<TSchema>;import { createDrizzleAdapter } from '@ereo/db-drizzle';
const adapter = createDrizzleAdapter({
driver: 'postgres-js',
url: process.env.DATABASE_URL!,
schema: {
users,
posts,
},
logger: process.env.NODE_ENV === 'development',
});Configuration Helpers
defineDrizzleConfig
Type-safe generic configuration builder:
function defineDrizzleConfig<T extends DrizzleConfig>(config: T): T;import { defineDrizzleConfig } from '@ereo/db-drizzle';
const config = defineDrizzleConfig({
driver: 'postgres-js',
url: process.env.DATABASE_URL!,
schema,
connection: {
ssl: 'require',
max: 10,
},
});Driver-Specific Config Presets
definePostgresConfig
PostgreSQL with sensible defaults:
function definePostgresConfig(
config: Omit<PostgresConfig, 'driver'>
): PostgresConfig;import { definePostgresConfig } from '@ereo/db-drizzle';
const config = definePostgresConfig({
url: process.env.DATABASE_URL!,
schema,
connection: {
ssl: 'require',
max: 20,
idle_timeout: 30,
connect_timeout: 10,
prepare: true,
},
});defineNeonHttpConfig
Neon HTTP driver (edge-compatible):
function defineNeonHttpConfig(
config: Omit<NeonHttpConfig, 'driver'>
): NeonHttpConfig;import { defineNeonHttpConfig } from '@ereo/db-drizzle';
const config = defineNeonHttpConfig({
url: process.env.DATABASE_URL!,
schema,
neon: {
fetchOptions: {
cache: 'no-store',
},
},
});defineNeonWebSocketConfig
Neon WebSocket driver (edge-compatible, better for real-time):
function defineNeonWebSocketConfig(
config: Omit<NeonWebSocketConfig, 'driver'>
): NeonWebSocketConfig;import { defineNeonWebSocketConfig } from '@ereo/db-drizzle';
const config = defineNeonWebSocketConfig({
url: process.env.DATABASE_URL!,
schema,
pool: {
max: 5,
idleTimeoutMs: 10000,
},
});definePlanetScaleConfig
PlanetScale serverless driver:
function definePlanetScaleConfig(
config: Omit<PlanetScaleConfig, 'driver'>
): PlanetScaleConfig;import { definePlanetScaleConfig } from '@ereo/db-drizzle';
const config = definePlanetScaleConfig({
url: process.env.DATABASE_URL!,
schema,
planetscale: {
fetch: customFetch, // Optional custom fetch
},
});defineLibSQLConfig
LibSQL/Turso configuration:
function defineLibSQLConfig(
config: Omit<LibSQLConfig, 'driver'>
): LibSQLConfig;import { defineLibSQLConfig } from '@ereo/db-drizzle';
const config = defineLibSQLConfig({
url: 'libsql://mydb.turso.io',
authToken: process.env.TURSO_AUTH_TOKEN!,
schema,
syncUrl: 'https://sync.turso.io', // Optional for embedded replicas
});defineBunSQLiteConfig
Bun's native SQLite (Bun runtime only):
function defineBunSQLiteConfig(
config: Omit<BunSQLiteConfig, 'driver'>
): BunSQLiteConfig;import { defineBunSQLiteConfig } from '@ereo/db-drizzle';
const config = defineBunSQLiteConfig({
url: './data/app.db',
schema,
pragma: {
journal_mode: 'WAL',
synchronous: 'NORMAL',
foreign_keys: true,
cache_size: 10000,
},
});defineBetterSQLite3Config
better-sqlite3 for Node.js:
function defineBetterSQLite3Config(
config: Omit<BetterSQLite3Config, 'driver'>
): BetterSQLite3Config;import { defineBetterSQLite3Config } from '@ereo/db-drizzle';
const config = defineBetterSQLite3Config({
url: './data/app.db',
schema,
options: {
readonly: false,
fileMustExist: false,
timeout: 5000,
verbose: console.log,
},
});defineD1Config
Cloudflare D1:
function defineD1Config(
config: Omit<D1Config, 'driver'>
): D1Config;import { defineD1Config } from '@ereo/db-drizzle';
const config = defineD1Config({
url: 'd1://', // Not used, d1 binding provided separately
schema,
d1: env.DB, // D1Database binding from Cloudflare
});defineEdgeConfig
Automatically configures edge-compatible settings based on driver:
function defineEdgeConfig(options: EdgeConfigOptions): DrizzleConfig;import { defineEdgeConfig } from '@ereo/db-drizzle';
const config = defineEdgeConfig({
driver: 'neon-http', // or 'planetscale', 'libsql', 'd1'
url: process.env.DATABASE_URL!,
schema,
authToken: process.env.TURSO_TOKEN, // For libsql
});Runtime Detection
detectRuntime
Detect the current JavaScript runtime:
function detectRuntime(): RuntimeEnvironment;import { detectRuntime } from '@ereo/db-drizzle';
const runtime = detectRuntime();
// 'bun' | 'node' | 'cloudflare-workers' | 'vercel-edge' | 'deno' | 'unknown'
if (runtime === 'cloudflare-workers') {
// Use D1 or other edge-compatible driver
}isEdgeRuntime
Check if running in an edge environment:
function isEdgeRuntime(): boolean;import { isEdgeRuntime, defineEdgeConfig, definePostgresConfig } from '@ereo/db-drizzle';
const config = isEdgeRuntime()
? defineEdgeConfig({ driver: 'neon-http', url: env.DATABASE_URL, schema })
: definePostgresConfig({ url: env.DATABASE_URL, schema });suggestDrivers
Get driver recommendations for the current environment:
function suggestDrivers(): DrizzleDriver[];import { suggestDrivers } from '@ereo/db-drizzle';
const suggested = suggestDrivers();
console.log(suggested); // ['neon-http', 'planetscale', 'libsql'] on edgeConfiguration Types
DrizzleDriver
type DrizzleDriver =
| 'postgres-js' // PostgreSQL via postgres.js
| 'neon-http' // Neon serverless HTTP
| 'neon-websocket' // Neon serverless WebSocket
| 'planetscale' // PlanetScale serverless
| 'libsql' // LibSQL/Turso
| 'bun-sqlite' // Bun's native SQLite
| 'better-sqlite3' // better-sqlite3 for Node
| 'd1'; // Cloudflare D1Edge Compatibility
import { EDGE_COMPATIBLE_DRIVERS } from '@ereo/db-drizzle';
const isEdge = EDGE_COMPATIBLE_DRIVERS['neon-http']; // true
const isNotEdge = EDGE_COMPATIBLE_DRIVERS['postgres-js']; // falsePostgresConfig
interface PostgresConfig extends DrizzleBaseConfig {
driver: 'postgres-js';
connection?: {
ssl?: boolean | 'require' | 'prefer' | 'allow';
max?: number;
idle_timeout?: number;
connect_timeout?: number;
prepare?: boolean;
};
}NeonHttpConfig
interface NeonHttpConfig extends DrizzleBaseConfig {
driver: 'neon-http';
neon?: {
fetchOptions?: RequestInit;
};
}NeonWebSocketConfig
interface NeonWebSocketConfig extends DrizzleBaseConfig {
driver: 'neon-websocket';
pool?: PoolConfig;
}PlanetScaleConfig
interface PlanetScaleConfig extends DrizzleBaseConfig {
driver: 'planetscale';
planetscale?: {
fetch?: typeof fetch;
};
}LibSQLConfig
interface LibSQLConfig extends DrizzleBaseConfig {
driver: 'libsql';
authToken?: string;
syncUrl?: string;
}BunSQLiteConfig
interface BunSQLiteConfig extends DrizzleBaseConfig {
driver: 'bun-sqlite';
pragma?: {
journal_mode?: 'DELETE' | 'TRUNCATE' | 'PERSIST' | 'MEMORY' | 'WAL' | 'OFF';
synchronous?: 'OFF' | 'NORMAL' | 'FULL' | 'EXTRA';
foreign_keys?: boolean;
cache_size?: number;
};
}BetterSQLite3Config
interface BetterSQLite3Config extends DrizzleBaseConfig {
driver: 'better-sqlite3';
options?: {
readonly?: boolean;
fileMustExist?: boolean;
timeout?: number;
verbose?: (message: string) => void;
};
}D1Config
interface D1Config extends DrizzleBaseConfig {
driver: 'd1';
d1?: D1Database; // Cloudflare D1 binding
}D1 Types
import type {
D1Database,
D1PreparedStatement,
D1Result,
D1ExecResult,
} from '@ereo/db-drizzle';Using with EreoJS
Basic Setup
// ereo.config.ts
import { defineConfig } from '@ereo/core';
import { createDatabasePlugin } from '@ereo/db';
import { createDrizzleAdapter, definePostgresConfig } from '@ereo/db-drizzle';
import * as schema from './schema';
export default defineConfig({
plugins: [
createDatabasePlugin(
createDrizzleAdapter(
definePostgresConfig({
url: process.env.DATABASE_URL!,
schema,
})
)
),
],
});In Loaders
// routes/posts/page.tsx
import { createLoader } from '@ereo/data';
import { useDb } from '@ereo/db';
import { posts } from '~/schema';
import { eq, desc } from 'drizzle-orm';
export const loader = createLoader({
load: async ({ context }) => {
const db = useDb(context);
const allPosts = await db.client
.select()
.from(posts)
.where(eq(posts.published, true))
.orderBy(desc(posts.createdAt));
return { posts: allPosts };
},
});In Actions
import { createAction } from '@ereo/data';
import { useDb, withTransaction } from '@ereo/db';
export const action = createAction({
handler: async ({ context, formData }) => {
return withTransaction(context, async (tx) => {
const post = await tx
.insert(posts)
.values({
title: formData.get('title') as string,
content: formData.get('content') as string,
})
.returning();
return { post: post[0] };
});
},
});Raw SQL Queries
import { useDb } from '@ereo/db';
export const loader = createLoader({
load: async ({ context }) => {
const db = useDb(context);
// Raw query with deduplication
const result = await db.query(
'SELECT * FROM posts WHERE created_at > $1',
[new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)]
);
return { recentPosts: result.result.rows };
},
});Environment-Based Configuration
Multi-Environment Setup
// db-config.ts
import {
definePostgresConfig,
defineBunSQLiteConfig,
defineEdgeConfig,
detectRuntime,
isEdgeRuntime,
} from '@ereo/db-drizzle';
import * as schema from './schema';
export function getDatabaseConfig() {
const runtime = detectRuntime();
// Edge runtime (Vercel Edge, Cloudflare Workers)
if (isEdgeRuntime()) {
return defineEdgeConfig({
driver: 'neon-http',
url: process.env.DATABASE_URL!,
schema,
});
}
// Bun runtime - use SQLite for dev, Postgres for prod
if (runtime === 'bun') {
if (process.env.NODE_ENV === 'development') {
return defineBunSQLiteConfig({
url: './data/dev.db',
schema,
});
}
}
// Default: PostgreSQL
return definePostgresConfig({
url: process.env.DATABASE_URL!,
schema,
});
}Cloudflare Workers with D1
// ereo.config.ts
import { defineConfig } from '@ereo/core';
import { createDatabasePlugin } from '@ereo/db';
import { createDrizzleAdapter, defineD1Config } from '@ereo/db-drizzle';
import * as schema from './schema';
interface Env {
DB: D1Database;
}
export default defineConfig({
plugins: [
{
name: 'db',
async setup(env: Env) {
return createDatabasePlugin(
createDrizzleAdapter(
defineD1Config({
url: 'd1://',
schema,
d1: env.DB,
})
)
);
},
},
],
});Complete Examples
PostgreSQL Full Setup
// schema.ts
import { pgTable, serial, varchar, text, timestamp, integer } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
email: varchar('email', { length: 255 }).notNull().unique(),
name: varchar('name', { length: 255 }),
createdAt: timestamp('created_at').defaultNow(),
});
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
title: varchar('title', { length: 255 }).notNull(),
content: text('content'),
authorId: integer('author_id').references(() => users.id),
createdAt: timestamp('created_at').defaultNow(),
});
// types.d.ts
declare module '@ereo/db' {
interface DatabaseTables {
users: typeof import('./schema').users;
posts: typeof import('./schema').posts;
}
}
// ereo.config.ts
import { defineConfig } from '@ereo/core';
import { createDatabasePlugin } from '@ereo/db';
import { createDrizzleAdapter, definePostgresConfig } from '@ereo/db-drizzle';
import * as schema from './schema';
export default defineConfig({
plugins: [
createDatabasePlugin(
createDrizzleAdapter(
definePostgresConfig({
url: process.env.DATABASE_URL!,
schema,
connection: {
ssl: 'require',
max: 10,
},
})
),
{
debug: process.env.NODE_ENV === 'development',
}
),
],
});
// routes/users/[id]/page.tsx
import { createLoader, createAction } from '@ereo/data';
import { useDb, withTransaction } from '@ereo/db';
import { users, posts } from '~/schema';
import { eq } from 'drizzle-orm';
export const loader = createLoader({
load: async ({ params, context }) => {
const db = useDb(context);
const user = await db.client
.select()
.from(users)
.where(eq(users.id, parseInt(params.id)))
.limit(1);
if (!user[0]) {
throw new Response('Not Found', { status: 404 });
}
const userPosts = await db.client
.select()
.from(posts)
.where(eq(posts.authorId, user[0].id));
return { user: user[0], posts: userPosts };
},
});
export const action = createAction({
handler: async ({ params, context, formData }) => {
return withTransaction(context, async (tx) => {
await tx
.update(users)
.set({ name: formData.get('name') as string })
.where(eq(users.id, parseInt(params.id)));
return { success: true };
});
},
});Turso Edge Setup
// ereo.config.ts
import { createDrizzleAdapter, defineLibSQLConfig } from '@ereo/db-drizzle';
export default defineConfig({
plugins: [
createDatabasePlugin(
createDrizzleAdapter(
defineLibSQLConfig({
url: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN!,
schema,
// Optional: sync for offline capabilities
syncUrl: process.env.TURSO_SYNC_URL,
})
)
),
],
});Re-exports from @ereo/db
For convenience, @ereo/db-drizzle re-exports commonly used functions:
import {
// Core functions
createDatabasePlugin,
useDb,
useAdapter,
getDb,
withTransaction,
// Types
DatabaseAdapter,
RequestScopedClient,
QueryResult,
MutationResult,
DedupResult,
DedupStats,
TransactionOptions,
} from '@ereo/db-drizzle';Related
- @ereo/db - Core database abstractions
- @ereo/db-surrealdb - SurrealDB adapter
- Drizzle ORM Documentation