12k
All articles

Drizzleによるスキーマファーストなデータベース開発

スキーマファーストのDrizzle ORM開発では、TypeScriptを信頼できる唯一の情報源とし、データベース構造とアプリケーションの型を整合させることで実行時の不一致を防ぐ。

OpenReplay Team
OpenReplay Team
Drizzleによるスキーマファーストなデータベース開発

TypeScriptの型定義は一つのことを示し、データベースは別のことを示す。クエリは実行時に失敗し、本来存在すべきでない不一致のデバッグに追われる。

Drizzleを使ったスキーマファーストなデータベース設計は、TypeScriptコードを唯一の信頼できる情報源とすることで、この問題を解決します。スキーマ定義がアプリケーションの型とデータベース構造の両方を駆動し、実行時のサプライズを引き起こす断絶を排除します。

この記事では、Drizzle ORMのスキーマファースト開発がどのように機能するか、異なるマイグレーションワークフローをいつ使用すべきか、そして避けるべき一般的な落とし穴について説明します。

重要なポイント

  • Drizzle ORMはコードファーストのアプローチを採用しており、TypeScriptのスキーマ定義が型、クエリ、データベース構造の唯一の信頼できる情報源となります。
  • Drizzle Kitは2つのマイグレーションパスを提供します:pushは迅速なローカル反復用、generate/migrateは監査可能でチームセーフなデプロイ用です。
  • 既存のデータベースを持つブラウンフィールドプロジェクトでは、drizzle-kit pullを使用してスキーマをイントロスペクトし、テーブルの再作成を避けるためにマイグレーションを生成します。
  • Drizzleの決定論的な命名と手動で作成されたデータベース間の制約名の不一致は、予期しないマイグレーション文を引き起こす可能性があります。

Drizzleにおけるスキーマファーストの実際の意味

Drizzleは基本的にコードファーストです。TypeScriptでテーブル、カラム、リレーションシップを定義すると、その定義が下流のすべて(クエリ、マイグレーション、型推論)に対して権威あるものとなります。

import { pgTable, serial, text, timestamp } from "drizzle-orm/pg-core"

export const posts = pgTable("posts", {
  id: serial().primaryKey(),
  title: text().notNull(),
  content: text(),
  createdAt: timestamp().defaultNow(),
})

このスキーマファイルは二重の目的を果たします。Drizzle ORMは型安全なクエリのためにこれを使用します。Drizzle Kitはデータベースの変更を生成または適用するためにこれを使用します。

ここでの「スキーマファースト」という用語は、TypeScriptの定義が先導することを意味します。データベースはそれに従います。

コードファースト vs データベースファースト:違いを理解する

従来のデータベースファーストのワークフローは、データベースを権威あるものとして扱います。手動またはSQLスクリプトでテーブルを作成し、その後既存の構造からアプリケーションコードを生成します。

コードファーストとデータベースファーストの比較では、これらの用語がしばしば混同されます。Drizzleの文脈では:

  • コードファースト: TypeScriptスキーマがデータベースの変更を駆動
  • データベースファースト: drizzle-kit pullを使用して既存のデータベース構造をTypeScriptに取り込む

Drizzleは両方をサポートしています。グリーンフィールドプロジェクトでは、コードファーストが通常よりクリーンです。ブラウンフィールドシナリオ(既存のデータベースを持つプロジェクトへの参加)では、pullによるイントロスペクションで迅速に開始できます。

Drizzle Kitのワークフロー:Push vs Generate

Drizzle Kitは、スキーマの変更をデータベースに適用するための2つの主要なパスを提供します。

迅速な反復のためのスキーマPush

npx drizzle-kit push

Pushはスキーマをデータベースと比較し、変更を直接適用します。マイグレーションファイルはなく、レビューステップもありません。

これは以下の場合に適しています:

  • ソロプロジェクト
  • 初期のプロトタイピング
  • 再作成可能なローカル開発データベース

トレードオフは可視性です。何が変更されたか、いつ変更されたかの記録は残りません。

チームの安全性のための生成されたマイグレーション

npx drizzle-kit generate
npx drizzle-kit migrate

generateはSQLマイグレーションファイルを作成します。migrateはそれらを適用します。ファイルはバージョン管理に保存され、監査証跡を作成します。

このアプローチは以下に適しています:

  • 変更のレビューが必要なチーム環境
  • ロールバック機能が必要な本番デプロイメント
  • 変更のドキュメントが必要なコンプライアンスシナリオ

どちらの方法も普遍的に正しいわけではありません。Drizzle Kitのワークフローは、コンテキストに基づいて両方に対応します。

Drizzle Kitの設定

drizzle.config.tsは、スキーマの場所とマイグレーションの保存場所をDrizzle Kitに伝えます:

import { defineConfig } from "drizzle-kit"

export default defineConfig({
  dialect: "postgresql",
  schema: "./src/db/schema.ts",
  out: "./drizzle/migrations",
  dbCredentials: {
    url: process.env.DATABASE_URL!,
  },
})

DrizzleはPostgreSQL、MySQL、SQLite/libSQLを含む主要なSQLデータベースをサポートしており、MSSQLやCockroachDBなどの追加の方言も登場しています。設定はデータベース間でほぼ一貫しており、方言とドライバー固有のオプションのみが変わります。

避けるべき一般的な落とし穴

Drizzleによるスキーマファースト開発には、摩擦点がないわけではありません。

既存のテーブルには慎重な取り扱いが必要です。 既存のテーブルを持つデータベースでDrizzleを採用する場合、最初のgenerateはすべてを再作成しようとするマイグレーションを生成する可能性があります。まずpullを使用して、スキーマファイルを現実と同期させてください。

イントロスペクションはノイズの多い差分を生成する可能性があります。 データベースからプルすると、手動で書くものとは異なる制約名やデフォルト値が含まれる可能性があります。その後のgenerateは、意味のある変更がなくても、これらを変更としてフラグ付けする可能性があります。

制約名の不一致は予期しないマイグレーションを引き起こします。 Drizzleは制約名を決定論的に生成します。データベースに異なる名前がある場合(手動作成または別のツールから)、動作を変更せずに制約の名前を変更するALTER文が表示されます。

実行時マイグレーションにはエラーハンドリングが必要です。 デプロイ中にプログラムでマイグレーションを適用する場合は、適切なエラーハンドリングでラップしてください。デプロイ中のマイグレーションの失敗は、データベースを不整合な状態にする可能性があります。

スキーマファーストがワークフローを改善する場合

Drizzle ORMのスキーマファースト開発は以下の場合に優れています:

  • クエリがスキーマと一致することをコンパイル時に保証したい場合
  • 複数の開発者がデータベースの変更を調整する必要がある場合
  • バンドルサイズが重要なエッジまたはサーバーレス環境にデプロイする場合
  • 抽象化されたクエリビルダーよりもSQLライクな構文を好む場合

Drizzle Studioは、データの閲覧とスキーマに対するクエリのテストのためのビジュアルインターフェースで、このワークフローを補完します。

まとめ

スキーマファイルから始めましょう。ドメインに一致するテーブルを定義します。ローカルで反復している間はpushを使用し、チームメイトを巻き込んだり本番環境にデプロイしたりする前にgeneratemigrateに切り替えます。

目標は一つのワークフローを永遠に選ぶことではなく、トレードオフを理解して開発の各フェーズに適したツールを選択することです。

よくある質問

データを失うことなく既存のデータベースでDrizzleを使用できますか?

はい。drizzle-kit pullを実行して既存のデータベースをイントロスペクトし、一致するTypeScriptスキーマを生成します。これにより破壊的なマイグレーションを回避できます。スキーマファイルが現在のデータベースの状態を反映したら、データ損失のリスクなしに、今後のすべての変更にgenerateとmigrateの使用を開始できます。

Drizzle Kitでpushからgenerateにいつ切り替えるべきですか?

プロジェクトがソロプロトタイピングを超えたら、generateに切り替えます。他の開発者が関与したり、ステージングまたは本番環境にデプロイしたりするようになったら、マイグレーションファイルは必要な監査証跡とレビュープロセスを提供します。Pushは、データベースを安全に再作成できるローカル開発に最適です。

Drizzle ORMは外部キーやJOINのようなリレーションシップをサポートしていますか?

はい。Drizzleはスキーマで直接外部キーを定義することをサポートし、1対1、1対多、多対多のリレーションシップを宣言するためのrelations APIを提供します。これらのリレーションはリレーショナルクエリAPIを強化し、生のSQL JOINを書くことなく、型安全な方法でネストされたデータを取得できます。

スキーマファースト開発においてDrizzleはPrismaとどう比較されますか?

両方ともコードファーストのORMですが、哲学が異なります。Prismaは独自のスキーマ言語を使用し、そこからクライアントを生成します。DrizzleはプレーンなTypeScriptでスキーマを定義し、SQL出力とより小さなバンドルサイズを直接制御できます。Drizzleは設計上SQLに近く、Prismaはそれをより抽象化します。

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.