Drizzleのマイグレーションファイルのコンフリクトを無くし実運用に乗せる仕組み
はじめに
こんにちは。株式会社トリドリでバックエンドエンジニアをしている松田です!
前回DrizzleORMについて、概要理解〜設定して使えるようになるまでを以下の記事で書きました。
この記事にて、drizzle-kitを使ったチーム開発では、マイグレーションファイルが頻繁に衝突する懸念があることが判明し、そのままでは開発体験が悪いという課題が残りました。
今回はその課題に対して解決策を提案する記事となります!
drizzleのマイグレーションをうまく使いこなしていきましょう!
達成したいこと
- drizzle-kitを使って各環境ごとにマイグレーションファイルのコンフリクトが起こらずチーム開発が進められる状態を作ること
課題と解決策の認識
<drizzleマイグレーションについて>
まず先に、drizzle-kitのマイグレーションについて前提情報を共有します。
drizzle-kitのマイグレーション機能を通してデータベースを更新する方法は主に2つあります。
-
(1)マイグレーションファイルを作成してからDBに変更を反映する方法
- schemaを変更(schema.tsを書き換える)
-
drizzle-kit generate
を実行しマイグレーションファイルを生成 -
drizzle-kit migrate
を実行しDBに変更を反映
-
(2)スキーマの状態を強制的にDBに反映する方法
- schemaを変更
-
drizzle-kit push
を実行しDBに変更を反映
<課題の認識>
drizzle導入当初は各々の開発者がPR作成前に上記(1)のやり方で実装を進めていました。しかし、このやり方だと、マイグレーションファイル生成コマンドの実行によって書き換わるファイル(_journal.json)のコンフリクトが発生し、解消が面倒でした。
前回の記事もご参考ください。
<解決策の認識>
上記課題を解決するために、マイグレーションファイル生成コマンド(drizzle-kit generate
)と、DBマイグレーション実行コマンド(drizzle-kit migrate
)の実行箇所とタイミングを分割しました。
<変更前>
対象 | 実行箇所 | コミット先 | タイミング |
---|---|---|---|
マイグレーションファイル生成 | 各開発者の環境 | 各開発者のブランチ | 各開発者の開発時 |
DBマイグレーション | 各開発者の環境 | - | 各開発者の開発時 |
<変更後>
対象 | 実行箇所 | コミット先 | タイミング |
---|---|---|---|
マイグレーションファイル生成 | GitHubActions | developブランチ | AWS環境デプロイ用のPRが作成もしくは更新された時 |
DBマイグレーション | AWS CodePipeline | - | AWS環境デプロイ用のPRがマージされた時 |
・GitHubActions上でどうやってマイグレーションファイルを生成するのか?
・マイグレーションファイル生成を各開発者が行わないとなると開発時はどう進めるのか?
これらの疑問を含め、以下の通り段階を踏んで実装を行なっていきます。
- ステージング環境と本番環境用にマイグレーション関連のディレクトリを分け、設定ファイルを用意する
- マイグレーションファイル生成を行うGitHubAction workflowを作成する
- 各開発者の環境ではgenerateとmigrateを利用しない運用にする
解決策の実装
1. AWS環境デプロイ用にマイグレーション関連のディレクトリを分け、設定ファイルを用意する
ここでは、ステージングと本番環境用にディレクトリを用意、drizzle設定ファイルを変更、package.jsonのscript変更の順で行なっていきます。
1-1. ステージングと本番環境用にディレクトリを用意
フォルダ構成
.
└── front/
├── app/
│ └── db/
│ ├── drizzle/
│ │ ├── prod/
│ │ │ ├── meta/
│ │ └── xdev/
│ │ ├── meta/
│ ├── index.ts
│ ├── schema.ts
│ └── seed.ts
├── drizzle.config.local.ts
├── drizzle.config.prod.ts
└── drizzle.config.xdev.ts
1-2. drizzle設定ファイルを変更
drizzle.config.prod.ts
drizzle.config.xdev.ts
を作成し以下の通り書きます。
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mysql",
schema: "./app/db/schema.ts",
out: "./app/db/drizzle/prod",
dbCredentials: {
user: process.env.DATABASE_USERNAME,
password: process.env.DATABASE_PASSWORD,
host: String(process.env.DATABASE_HOST),
port: Number(process.env.DATABASE_PORT),
database: String(process.env.DATABASE_NAME),
},
});
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mysql",
schema: "./app/db/schema.ts",
out: "./app/db/drizzle/xdev",
dbCredentials: {
user: process.env.DATABASE_USERNAME,
password: process.env.DATABASE_PASSWORD,
host: String(process.env.DATABASE_HOST),
port: Number(process.env.DATABASE_PORT),
database: String(process.env.DATABASE_NAME),
},
});
1-3. package.jsonのscript変更
{
"scripts": {
"drizzle:generate:xdev": "npx drizzle-kit generate --config=drizzle.config.xdev.ts",
"drizzle:generate:prod": "npx drizzle-kit generate --config=drizzle.config.prod.ts",
"drizzle:migrate:xdev": "npx drizzle-kit migrate --config=drizzle.config.xdev.ts",
"drizzle:migrate:prod": "npx drizzle-kit migrate --config=drizzle.config.prod.ts",
// 諸々
},
// 諸々
}
準備は完了です。あとはDBマイグレーションを実行するCIを用意します。
2. マイグレーションファイル生成を行うGitHubAction workflowを作成する
ここではGitHub Actionsを使用して、開発or本番リリース用のPullRequestが作成or更新された時にマイグレーションファイルを生成するように設定を組みます。
# MEMO: main or dev_envに向けてPRが作成された時、各環境で分けたディレクトリにmigrationファイルを作成し
# developブランチに対してコミットを実行するワークフロー
name: Generate migration file
on:
pull_request:
types: [opened, synchronize]
branches:
- main
- dev_env
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 0
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
- name: Install dependencies
working-directory: ./front
run: npm ci
- name: Run npm script for main branch
if: ${{ github.event.pull_request.base.ref == 'main' }}
working-directory: ./front
run: npm run drizzle:generate:prod
- name: Run npm script for dev_env branch
if: ${{ github.event.pull_request.base.ref == 'dev_env' }}
working-directory: ./front
run: npm run drizzle:generate:xdev
- name: Configure git
run: |
git config user.name 'github-actions[bot]'
git config user.email 'github-actions[bot]@users.noreply.github.com'
- name: Commit and push changes
run: |
git add .
if git diff-index --quiet HEAD --; then
echo "No changes to commit."
else
git commit -m "Auto-generated migration file"
git push origin ${{ github.event.pull_request.head.ref }}
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
上記ワークフローファイルが動作すると、schemaに差分があった際に以下のようなコミットが自動で作られます。
3. 各開発者の環境ではgenerateとmigrateを利用しない運用にする
各開発者の環境でマイグレーション履歴を作らない方針でいきます。
マイグレーションを残さず、schemaの内容を直接ローカルDBに反映させることができるため、手間が少なくて済むと考えたためです。こちらはdrizzleが用意している「push」を使うことで可能となります。
Dockerを使っているため、まずdrizzle.config.tsから変更し、package.jsonのscript変更、Dockerfileのプロジェクト立ち上げコマンドの変更の順で行なっていきます。
3-1. drizzle.config.tsの変更
以前用意していた drizzle.config.ts
ファイルを drizzle.config.local.ts
に名称変更し、中身を書き換えます。
import dotenv from "dotenv";
import { defineConfig } from "drizzle-kit";
dotenv.config();
export default defineConfig({
dialect: "mysql",
schema: "./app/db/schema.ts",
out: "./app/db/drizzle/local",
dbCredentials: {
user: process.env.DATABASE_USERNAME,
password: process.env.DATABASE_PASSWORD,
host: String(process.env.DATABASE_HOST),
port: Number(process.env.DATABASE_PORT),
database: String(process.env.DATABASE_NAME),
},
});
outの部分は実際使っていないですが、設定として書く必要があるため一応書いています。
3-2. package.jsonのscript変更
pushを実行するコマンドを、先ほど編集したconfigファイル指定の上用意します。
{
"scripts": {
"drizzle:push": "npx drizzle-kit push --config=drizzle.config.local.ts",
// 諸々
},
// 諸々
}
3-3. Docerfileのコマンド変更
開発用に用意している Dockerfile.dev
を先ほど用意したコマンドを使うようにします。
FROM node:20-alpine
WORKDIR /usr/src/app
# MEMO: パッケージを新しくした場合は、npm ci ではなく npm install を使う
CMD ["sh", "-c", "npm ci && npm run drizzle:push && npm run drizzle:seed:dev && npm run dev"]
EXPOSE 5173
これで完成です!!お疲れ様でした!!
余談
はじめはPullRequestがマージされた後にマイグレーションファイルを作成してコミット、加えてPullRequestを作成しマージするようなCIにしていたのですが、本番ブランチに対してはbranch protectionにより自動マージがはじかれてしまいました。
自動マージができずコミットまでとなると、手動でPullRequestを作成しマージをするという手間が発生します。この手間を改善した仕様が、PullRequest作成時にコミットを行う上記のmigration.ymlとなります。
こうすることでPullRequestを手動作成する手間がなくなりました。
まとめ
ここまで読んでいただきありがとうございました!
今回をもってdrizzleを実運用で使うための設定が完了しました。
軽量なORMであるが故に、細かな設定までは用意されていないのがdrizzleなのだなと感じました。
もちろん軽量であることで、キャッチアップが早く取り組みやすい、クエリはSQLライクなので書きやすいといったメリットもあります!小〜中規模のプロジェクトであれば個人的に使っていきたいなと思ったORMです。
ぜひ皆さんも使ってみてください!
参考資料
-
マイグレーションマージコンフリクトについての、GitHub discussion
https://github.com/drizzle-team/drizzle-orm/discussions/1104 -
drizzleドキュメント
https://orm.drizzle.team/kit-docs/conf
Discussion