Runner in the High

技術のことをかくこころみ

kyrage v1.0.0 をリリースした

前に紹介した TypeScript でスキーマ定義できるマイグレーションツール kyrage の v1.0.0 をリリースした。

izumisy.work

前回の時点ではテーブルのマイグレーションしか対応していなかったが、v1.0.0 ではもうちょい実用的なツールに仕上がったと思う。

以下、追加された主要な機能と変更点。

インデックスと制約のサポート

前回「今後やりたいこと」に挙げていた、index や外部キー制約がついに対応できた。

インデクスや制約を使うスキーマ定義は、defineConfig の第二引数で以下のように書ける:

import { column as c, defineTable as t } from "@izumisy/kyrage";

export const posts = t(
  "posts",
  {
    id: c("uuid"),
    author_id: c("uuid"),
    slug: c("text", { notNull: true }),
    title: c("text"),
    content: c("text", { notNull: true }),
  },
  (t) => [
    // 複合プライマリキー
    t.primaryKey(["id", "author_id"]),
    
    // ユニーク制約(カスタム名付き)
    t.unique(["author_id", "slug"], {
      name: "unique_author_slug",
    }),
    
    // 外部キー制約(CASCADE DELETE 付き)
    t.reference("author_id", members, "id", {
      onDelete: "cascade",
      name: "posts_author_fk"
    }),
    
    // インデックス
    t.index(["slug", "title"], { unique: true })
  ]
);

Dev Database サポート

Atlas の Dev Database という概念に近いものを実装した。

これは、本番データベースに影響を与えずにマイグレーションを生成するための機能。

# 開発用の一時的なデータベースコンテナを起動してマイグレーションを生成
$ kyrage generate --dev
🚀 Starting dev database for migration generation...
✔ Dev database started: postgres
-- create_table: users
   -> column: id ({"type":"uuid","primaryKey":true,"notNull":true})
   -> column: email ({"type":"text","notNull":true,"unique":true})
✔ Migration file generated: migrations/1755525514175.json
✔ Dev database stopped

内部的には以下のような処理が行われる:

  1. 新しい Docker コンテナでクリーンなデータベースを起動
  2. 既存のマイグレーションをすべて適用してベースラインを確立
  3. スキーマ定義とコンテナ上の状態を比較
  4. マイグレーションファイルを生成
  5. コンテナを自動的にクリーンアップ

設定ファイルでは以下のように定義できる:

export default defineConfig({
  database: {
    dialect: "postgres",
    connectionString: "psql://prod:pass@prod-db.com/myapp",
  },
  // 開発用データベース設定
  dev: {
    // Docker コンテナを使う場合
    container: {
      image: "postgres:17"
    },
    // または既存のデータベースを使う場合
    // connectionString: "psql://dev:pass@dev-db.com/myapp_dev"
  },
  tables: [/* ... */],
});

もともと、自前で Docker コンテナを立ち上げて connectionString を設定すれば同じようなことはできたのだが、コンテナの起動し忘れとか、既存マイグレーションのベースライン適用し忘れとか、そういう細かい手間がいちいち発生するのがダルい。作っている間も動作確認でよくあった。

Dev Database ならこれらが全部自動化される。複数の開発者が異なるスキーマ変更を並行して進めている場合でも、各自がクリーンな状態からマイグレーションを生成できる。本番DBの現在の状態に依存しないので、「誰かが先にマイグレーションを適用したから差分がおかしくなった」みたいな問題が起きないだろう。全員が同じ Docker イメージから始めるので、環境差異によるトラブルも起きにくい。

また、開発者に本番データベースへの接続情報を共有する必要がなくなる。これは地味だが重要で、本番環境へのアクセス権限を最小限に保てる。開発者は Dev Database だけで作業を完結できるので、誤って本番データを触ってしまうリスクもない。

ちなみに、内部的には Testcontainers を使って実装している。Testconatinersは超便利で、Github Actionsで動かす際も明示的にサービスの定義など不要で全部いい感じにしてくれるので素晴らしい。

環境別設定との組み合わせ

余談だが kyrage は設定の読み込みに unjs/c12 を採用しているので、NODE_ENV による設定の切り替えも可能。

例えば以下のような設定ができる:

export default defineConfig({
  // 開発環境: 各自のローカル Docker を使用
  $development: {
    database: {
      dialect: "postgres",
      connectionString: "postgres://dev:dev@localhost:5432/myapp_dev"
    },
    dev: {
      container: {
        image: "postgres:17"
      }
    }
  },
  
  // ステージング環境: 共有の Dev Database を使用
  $staging: {
    database: {
      dialect: "postgres",
      connectionString: "postgres://staging:pass@staging-db.internal/myapp_staging"
    },
    dev: {
      connectionString: "postgres://devdb:pass@shared-dev-db.internal/myapp_dev"
    }
  },
  
  // 本番環境: Dev Database なしで直接本番に接続(CI/CD 用)
  $production: {
    database: {
      dialect: "postgres",
      connectionString: process.env.DATABASE_URL!
    }
  },
  
  tables: [/* ... */]
});

これにより、チーム内での役割や環境に応じた柔軟な運用が可能になるだろう。

また、まだ構想段階だが、Dev Database コンテナを永続化する Reuse API という機能の追加も検討している。

現在は --devマイグレーション生成のたびにコンテナが作り直されるが、Reuse API を使えばコンテナを使い回せるようになる。これにより、テストデータを保持したまま開発を進められたり、アプリケーションから同じ Dev Database に接続できたりと、より実践的な開発フローが実現できるはずだ。kyrage dev get-url でコネクション URL を取得して DATABASE_URL=$(kyrage dev get-url) npm run dev のように環境変数に設定する、といった使い方も想定している。

なお、作者である自分はKyselyと組み合わせたくてこのツールを作ったということもあり、スキーマ定義を更新するごとに kyrage generate --dev --apply && DATABASE_URL="$(kysely dev get-url)" kysely-codegen を実行する、みたいなプロセスを妄想している。

まとめ

v1.0.0 では、前回の記事で「今後やりたいこと」として挙げていた主要な機能のうち、特に重要な index と外部キー制約の対応が完了した。さらに Dev Database サポートによって、チーム開発やCI/CDでの利用がかなり実用的になったと思われる。

まだ PostgreSQL 互換以外の DB 対応や down マイグレーションなど、やりたいことは残っているが、ひとまず v1.0.0 として区切りをつけることにした。

興味がある方はぜひ試してみてください。フィードバックや PR も歓迎です。

github.com