Drizzle ORMのデータベース接続の概要
Drizzle ORMは、TypeScriptベースのORMとして、サーバーレスやエッジ環境での利用を前提に設計されています。データベース接続には、node-postgres、MySQL、SQLiteといった主要なドライバーに対応し、各種クラウドサービスのサーバーレス環境に合わせた接続設定も提供しています。今回は、Drizzle ORMでの接続設定と、各データベースの使用例について詳しく紹介します。
Drizzle ORMがサポートするデータベース
Drizzle ORMは、以下のデータベースとドライバーをサポートしています:
PostgreSQL系
- node-postgres (pg): 標準的なNode.js PostgreSQLドライバー
- Neon: サーバーレスPostgreSQL(HTTP/WebSocket)
- Vercel Postgres: Vercel統合PostgreSQL
- Supabase: オープンソースFirebase代替
- AWS RDS Data API: Aurora Serverless用
- PGlite: ブラウザ内PostgreSQL(WASM)
MySQL系
- mysql2: 標準的なNode.js MySQLドライバー
- PlanetScale: サーバーレスMySQL(HTTP)
- AWS RDS Data API: Aurora Serverless MySQL用
SQLite系
- better-sqlite3: Node.js用高速同期SQLite
- Bun SQLite: Bunランタイム組み込みSQLite
- Cloudflare D1: Cloudflare Workers用サーバーレスSQLite
- Expo SQLite: React Native/Expo用
- op-sqlite: React Native用高性能SQLite
- libSQL (Turso): エッジ対応分散SQLite
データベース選択のガイドライン
| 環境/要件 | 推奨データベース | 理由 |
|---|---|---|
| Node.js サーバー | PostgreSQL (node-postgres), MySQL (mysql2) | 成熟したドライバー、豊富な機能 |
| Vercel/Next.js | Neon, Vercel Postgres, PlanetScale | サーバーレス対応、高速コールドスタート |
| Cloudflare Workers | D1, Neon (HTTP), Turso | エッジ環境で動作、低レイテンシ |
| React Native/Expo | Expo SQLite, op-sqlite | オフライン対応、デバイス内データ |
| ブラウザ/WASM | PGlite, SQLite WASM | クライアントサイド実行 |
| AWS Lambda | Neon, Aurora Serverless (Data API) | サーバーレス、接続プーリング不要 |
基本的な接続設定
PostgreSQL (node-postgres)
標準的なNode.jsサーバーでの接続方法です。
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
// 接続プールを作成
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 10, // 最大接続数
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000
});
// Drizzle ORMを初期化
export const db = drizzle(pool);
// アプリ終了時にクリーンアップ
process.on('SIGTERM', async () => {
await pool.end();
});
接続URL形式: postgresql://username:password@hostname:port/database?sslmode=require
MySQL (mysql2)
MySQLサーバーへの接続方法です。
import { drizzle } from 'drizzle-orm/mysql2';
import mysql from 'mysql2/promise';
// 接続プールを作成
const poolConnection = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
});
// Drizzle ORMを初期化
export const db = drizzle(poolConnection);
接続URL形式: mysql://username:password@hostname:port/database
SQLite (better-sqlite3)
ローカル開発やNode.jsアプリでの軽量データベースです。
import { drizzle } from 'drizzle-orm/better-sqlite3';
import Database from 'better-sqlite3';
// SQLiteデータベースファイルを開く
const sqlite = new Database('app.db');
// Drizzle ORMを初期化
export const db = drizzle(sqlite);
// WALモードを有効化(パフォーマンス向上)
sqlite.pragma('journal_mode = WAL');
スキーマ定義の例
各データベースに対応したスキーマ定義の例です。
// PostgreSQL
import { pgTable, serial, varchar, timestamp, boolean } from 'drizzle-orm/pg-core';
import { sql } from 'drizzle-orm';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: varchar('name', { length: 100 }).notNull(),
email: varchar('email', { length: 255 }).notNull().unique(),
is_active: boolean('is_active').notNull().default(true),
created_at: timestamp('created_at', { withTimezone: true })
.notNull()
.default(sql`now()`)
});
// MySQL
import { mysqlTable, int, varchar as mysqlVarchar, timestamp as mysqlTimestamp } from 'drizzle-orm/mysql-core';
export const usersMySQL = mysqlTable('users', {
id: int('id').primaryKey().autoincrement(),
name: mysqlVarchar('name', { length: 100 }).notNull(),
email: mysqlVarchar('email', { length: 255 }).notNull(),
created_at: mysqlTimestamp('created_at').defaultNow().notNull()
});
// SQLite
import { sqliteTable, integer, text } from 'drizzle-orm/sqlite-core';
export const usersSQLite = sqliteTable('users', {
id: integer('id').primaryKey({ autoIncrement: true }),
name: text('name').notNull(),
email: text('email').notNull(),
created_at: integer('created_at', { mode: 'timestamp' })
.notNull()
.default(sql`(unixepoch())`)
});
// TypeScript型推論
export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;
サーバーレス環境での接続
Neon (サーバーレスPostgreSQL)
エッジランタイムに最適なHTTP接続です。
import { neon } from '@neondatabase/serverless';
import { drizzle } from 'drizzle-orm/neon-http';
// Neon HTTPクライアントを作成
const sql = neon(process.env.DATABASE_URL!);
// Drizzle ORMを初期化
export const db = drizzle(sql);
// Cloudflare Workers、Vercel Edge Functionsで使用可能
特徴:
- コールドスタート時間: ~0ms
- 接続プーリング不要
- エッジ環境で最適なパフォーマンス
Node.js環境でトランザクションが必要な場合はWebSocket接続を使用:
import { Pool } from '@neondatabase/serverless';
import { drizzle } from 'drizzle-orm/neon-serverless';
import ws from 'ws';
// WebSocket設定(Node.jsの場合)
import { neonConfig } from '@neondatabase/serverless';
neonConfig.webSocketConstructor = ws;
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
export const db = drizzle(pool);
Vercel Postgres
Vercel統合のPostgreSQLサービスです。
import { sql } from '@vercel/postgres';
import { drizzle } from 'drizzle-orm/vercel-postgres';
// Vercel Postgres接続
export const db = drizzle(sql);
// Vercel環境変数が自動的に設定される
// POSTGRES_URL, POSTGRES_PRISMA_URL, POSTGRES_URL_NON_POOLING
特徴:
- Vercelプロジェクトとシームレスに統合
- 自動接続プーリング
- Edge FunctionsとNode.js両対応
PlanetScale (サーバーレスMySQL)
MySQL互換のサーバーレスデータベースです。
import { Client } from '@planetscale/database';
import { drizzle } from 'drizzle-orm/planetscale-serverless';
// PlanetScaleクライアントを作成
const client = new Client({
url: process.env.DATABASE_URL
});
// Drizzle ORMを初期化
export const db = drizzle(client);
特徴:
- HTTP接続(エッジ対応)
- ブランチング機能(本番環境を変更せずにスキーマ変更をテスト)
- 外部キー制約なし(アプリケーションレベルで管理)
Supabase (オープンソースFirebase代替)
PostgreSQLベースのBaaSプラットフォームです。
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
// Supabase接続
const client = postgres(process.env.DATABASE_URL!);
export const db = drizzle(client);
// Supabaseの認証・ストレージ・リアルタイム機能も利用可能
Turso (libSQL - エッジ対応分散SQLite)
エッジで動作する分散SQLiteデータベースです。
import { drizzle } from 'drizzle-orm/libsql';
import { createClient } from '@libsql/client';
// Tursoクライアントを作成
const client = createClient({
url: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN!
});
// Drizzle ORMを初期化
export const db = drizzle(client);
特徴:
- エッジロケーションに近いレプリカ
- SQLiteの互換性
- 低レイテンシ読み取り
エッジランタイムとモバイル向けSQLite
Cloudflare D1
Cloudflare Workers用のサーバーレスSQLiteです。
import { drizzle } from 'drizzle-orm/d1';
export interface Env {
DB: D1Database;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// D1データベースに接続
const db = drizzle(env.DB);
// クエリ実行
const users = await db.select().from(usersTable);
return Response.json(users);
}
};
特徴:
- グローバルに分散
- 無料プラン: 5GB、500万読み取り/月
- 自動レプリケーション
Bun SQLite
Bunランタイム組み込みのSQLiteです。
import { drizzle } from 'drizzle-orm/bun-sqlite';
import { Database } from 'bun:sqlite';
// Bunの高速SQLiteを使用
const sqlite = new Database('app.db');
export const db = drizzle(sqlite);
// Bunは他のランタイムより高速
Expo SQLite / op-sqlite
React Native / Expoアプリ用のSQLiteです。
// Expo SQLite
import { drizzle } from 'drizzle-orm/expo-sqlite';
import { openDatabaseSync } from 'expo-sqlite/next';
const expoDb = openDatabaseSync('app.db');
export const db = drizzle(expoDb);
// op-sqlite(より高速)
import { drizzle } from 'drizzle-orm/op-sqlite';
import { open } from '@op-engineering/op-sqlite';
const opsqliteDb = open({ name: 'app.db' });
export const db = drizzle(opsqliteDb);
用途: モバイルアプリのオフラインデータ管理
実践例:環境別の設定
開発環境と本番環境の切り替え
// db/index.ts
import { drizzle as drizzlePg } from 'drizzle-orm/node-postgres';
import { drizzle as drizzleNeon } from 'drizzle-orm/neon-http';
import { Pool } from 'pg';
import { neon } from '@neondatabase/serverless';
const isDev = process.env.NODE_ENV === 'development';
export const db = isDev
? drizzlePg(new Pool({ connectionString: process.env.DATABASE_URL }))
: drizzleNeon(neon(process.env.DATABASE_URL!));
環境変数の設定例
# .env.local (開発環境)
DATABASE_URL="postgresql://user:password@localhost:5432/mydb"
NODE_ENV="development"
# .env.production (本番環境)
DATABASE_URL="postgresql://user:password@aws-region.neon.tech/mydb?sslmode=require"
NODE_ENV="production"
drizzle.config.ts の設定
マイグレーションとスキーマ管理のための設定ファイルです。
// drizzle.config.ts
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './src/db/schema.ts',
out: './drizzle',
dialect: 'postgresql', // または 'mysql', 'sqlite'
dbCredentials: {
url: process.env.DATABASE_URL!
},
verbose: true,
strict: true
});
マイグレーションの実行:
# スキーマからマイグレーションを生成
npx drizzle-kit generate
# マイグレーションを適用
npx drizzle-kit migrate
# Drizzle Studioを起動(GUIデータ閲覧)
npx drizzle-kit studio
データベース接続のベストプラクティス
1. 環境変数での接続情報管理
接続情報はコード内に直接書き込まず、環境変数で管理します。
// ❌ 悪い例
const db = drizzle(new Pool({
host: 'localhost',
user: 'admin',
password: 'secret123' // パスワードをハードコーディング
}));
// ✅ 良い例
const db = drizzle(new Pool({
connectionString: process.env.DATABASE_URL
}));
2. 接続プーリングの適切な設定
Node.jsサーバーでは接続プールを使用します。
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 20, // 最大接続数(アプリケーションの負荷に応じて調整)
idleTimeoutMillis: 30000, // アイドル接続のタイムアウト
connectionTimeoutMillis: 2000, // 接続取得のタイムアウト
});
推奨値:
- 小規模アプリ: max = 10
- 中規模アプリ: max = 20
- 大規模アプリ: max = 50+
3. エラーハンドリング
データベース接続エラーを適切に処理します。
import { db } from './db';
import { users } from './schema';
export async function getUserSafe(id: number) {
try {
const user = await db.select().from(users).where(eq(users.id, id));
return { success: true, data: user[0] };
} catch (error: any) {
console.error('Database error:', error);
// PostgreSQLエラーコード
if (error.code === '23505') {
return { success: false, error: 'Duplicate key violation' };
}
if (error.code === 'ECONNREFUSED') {
return { success: false, error: 'Database connection refused' };
}
return { success: false, error: 'Database operation failed' };
}
}
4. タイムアウトの設定
サーバーレス環境では、適切なタイムアウト設定が重要です。
// Neon with timeout
const sql = neon(process.env.DATABASE_URL!, {
fetchOptions: {
cache: 'no-store'
},
fullResults: false
});
// クエリレベルのタイムアウト
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Query timeout')), 5000)
);
const result = await Promise.race([
db.select().from(users),
timeoutPromise
]);
5. サーバーレス環境でのセッション管理
サーバーレス環境では、リクエストごとに新しい接続を作成します。
// ❌ 悪い例:グローバルな接続プール(サーバーレスでは問題)
const pool = new Pool({ ... });
const db = drizzle(pool);
export default async function handler(req, res) {
// リクエスト間で接続が共有されない可能性がある
}
// ✅ 良い例:HTTP接続(サーバーレス最適化)
import { neon } from '@neondatabase/serverless';
const sql = neon(process.env.DATABASE_URL!);
const db = drizzle(sql);
export default async function handler(req, res) {
// リクエストごとに新しい接続
}
6. 接続リソースのクリーンアップ
Node.jsサーバーでは、アプリ終了時に接続をクリーンアップします。
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const db = drizzle(pool);
// Graceful shutdown
process.on('SIGTERM', async () => {
console.log('Closing database connections...');
await pool.end();
process.exit(0);
});
process.on('SIGINT', async () => {
console.log('Closing database connections...');
await pool.end();
process.exit(0);
});
まとめ
Drizzle ORMは、幅広いデータベースとランタイムに対応した柔軟なORMです。プロジェクトの要件に応じて最適なデータベースを選択できます。
主なポイント:
- 豊富なデータベースサポート: PostgreSQL、MySQL、SQLiteとその派生サービス
- サーバーレス最適化: Neon、PlanetScale、D1などエッジ対応
- 環境別設定: 開発、ステージング、本番環境を柔軟に切り替え
- 型安全性: TypeScriptの型推論による安全なデータ操作
- 接続管理: プーリング、タイムアウト、エラーハンドリングの最適化
- マイグレーション: drizzle-kitによるスキーマ管理
選択のガイドライン:
- Node.jsサーバー: PostgreSQL/MySQL with 接続プール
- エッジ/サーバーレス: Neon, PlanetScale, D1 (HTTP接続)
- モバイルアプリ: Expo SQLite, op-sqlite
- 低レイテンシ要件: Turso, D1(グローバル分散)
適切なデータベースとドライバーを選択することで、パフォーマンスとコストを最適化できます。