Documentation Drizzle

Drizzle ORMのデータベース接続の概要

Drizzle ORMは、TypeScriptベースのORMとして、サーバーレスやエッジ環境での利用を前提に設計されています。データベース接続には、node-postgresMySQLSQLiteといった主要なドライバーに対応し、各種クラウドサービスのサーバーレス環境に合わせた接続設定も提供しています。今回は、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.jsNeon, Vercel Postgres, PlanetScaleサーバーレス対応、高速コールドスタート
Cloudflare WorkersD1, Neon (HTTP), Tursoエッジ環境で動作、低レイテンシ
React Native/ExpoExpo SQLite, op-sqliteオフライン対応、デバイス内データ
ブラウザ/WASMPGlite, SQLite WASMクライアントサイド実行
AWS LambdaNeon, 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です。プロジェクトの要件に応じて最適なデータベースを選択できます。

主なポイント:

  1. 豊富なデータベースサポート: PostgreSQL、MySQL、SQLiteとその派生サービス
  2. サーバーレス最適化: Neon、PlanetScale、D1などエッジ対応
  3. 環境別設定: 開発、ステージング、本番環境を柔軟に切り替え
  4. 型安全性: TypeScriptの型推論による安全なデータ操作
  5. 接続管理: プーリング、タイムアウト、エラーハンドリングの最適化
  6. マイグレーション: drizzle-kitによるスキーマ管理

選択のガイドライン:

  • Node.jsサーバー: PostgreSQL/MySQL with 接続プール
  • エッジ/サーバーレス: Neon, PlanetScale, D1 (HTTP接続)
  • モバイルアプリ: Expo SQLite, op-sqlite
  • 低レイテンシ要件: Turso, D1(グローバル分散)

適切なデータベースとドライバーを選択することで、パフォーマンスとコストを最適化できます。

参考文献

円