対象: データベースの変更を、画面で手作業していて「本番に入ったの?」「誰がいつ変えたの?」が不安な方(エンジニアでなくてもOK)
所要時間: 60分前後 / 使うもの: Supabaseのアカウント(無料でOK)、GitHubのアカウント、ブラウザ
確認日: 2026-10-31(料金や仕様は変わります)
続きの記事(変更の状況が見えるボードにしてみた): https://qiita.com/tomozilla/items/9f175f4869e15c1417e6
ビジネス編(やさしい解説・note): https://note.com/tomozilla/n/n77dc7e7d68dc
データベースの変更を、ダッシュボードで手作業していると、こんな不安が出てきます。「あの変更、本番に入ったんだっけ?」「誰が、いつ、何を変えたの?」
この記事では、変更をSQLのファイルにしてGitHubに置き、マージ(取り込み)するとSupabaseのデータベースに自動で反映される仕組みを、つなぐ作業はダッシュボードの画面だけで試しました。トークンやパスワードをどこかに貼る作業はありません(ひな形ファイルを作るときに、コマンドを1回だけ使いました。0章)。さらに、わざと壊したらどうなるかも確かめました。私は同じように試している一人で、公式の人間ではありません。
0. 先に伝えておきたいこと
- 実際にやったのは、無料プロジェクトにGitHubのリポジトリをつなぎ、プルリクエスト(変更の提案)を何本かマージすること。この部分の費用は$0でした。
- 最後に、**有料(Proプラン)**のプロジェクトで「プレビューブランチ」も試しました。短時間(プロジェクトを約15分、ブランチを約2分)だけ動かして、すぐ削除しました。料金の目安は8章に書きます。
- 試していないこと:GitHub ActionsとSupabase CLIを組み合わせる方法(テスト・承認付き)。ネットで見かける定番の構成ですが、私は動かしていません(9章)。
- 手元のPCで
supabase initを1回だけ使いました(config.tomlというひな形ファイルを作るため)。ほかのSupabase CLIの操作は使っていません。2本目以降のプルリクの作成とマージは、1本目だけブラウザで、残りはghというGitHubのコマンドで行いました(画面での操作と結果は同じです)。 - 使ったデータはすべて架空です。
用語を1つだけ:この記事で「マイグレーション」と言うのは、データベースの変更を書いた1枚のSQLファイルのことです。ファイル名の頭の数字(日付)が、実行する順番になります。
1. 準備:リポジトリに supabase フォルダを置く
つなぐリポジトリには、次の形で supabase フォルダが必要です。
supabase/
config.toml ← 設定ファイル(supabase init で作れる)
migrations/
20261031000100_create_notes.sql
1枚目のマイグレーションは、小さな「notes」テーブルを作るだけの内容です。
create table public.notes (
id bigint generated always as identity primary key,
body text not null,
created_at timestamptz not null default now()
);
alter table public.notes enable row level security;
create policy "notes are readable by signed-in users"
on public.notes for select
to authenticated
using (true);
config.tomlを手で書いてもよいのか、GitHubの画面から作ってもよいのかは、私は試していません。supabase initで作ったものを使いました。
2. ダッシュボードでGitHubをつなぐ
Project Settings → Integrations → GitHub → 「Choose GitHub repository」 を開きます。
ここで私は少し迷いました。自分のリポジトリが一覧に出てこないのです。一覧の下の 「Add GitHub Repositories」 を押すと、GitHubの「Supabaseアプリをインストールしますか?」画面が開きました。
- 「All repositories」と「Only select repositories」があります。後者にして、この1つのリポジトリだけを選びました(アプリが求める権限は、コードの読み取りと、Actions・チェック・プルリクエストなどの読み書きでした)。
- 私の環境では、インストール後に画面が「Completing GitHub Authorization...」のまま止まりました。ダッシュボードを開き直したら、自分のリポジトリが一覧に出ました(別のタブで開いたせいかもしれません)。
リポジトリを選ぶと、次のフォームが出ます。
-
Working directory:
supabaseフォルダがある場所。リポジトリの直下なら.のままでOK。 -
Deploy to production:オンのままにすると、本番ブランチ(
main)にマージされたら、本番のデータベースに反映します。 - 無料プロジェクトでは、下の「Branching」(プルリクごとのお試し環境)は灰色で、Proプランが必要と出ます(7章)。
Enable integration を押すと、「GitHub connection updated」と出ました。ここまでで、設定は終わりです。
ここまで出ていれば成功です:ボタンが「Disable integration」に変わります。
この時点で、データベースはまだ空でした。リポジトリには1枚目のマイグレーションがあるのに、Database → Migrations には何もありません。つなぐ前から置いてあったファイルは、すぐには反映されないようです。
3. 1本目のプルリクをマージする
2枚目のマイグレーション(author列を足して、架空のサンプル2行を入れる)を、プルリクにしてマージしました。
alter table public.notes add column author text not null default 'unknown';
insert into public.notes (body, author) values
('First note written from a migration file', 'sample-user-a'),
('Second sample note', 'sample-user-b');
プルリクの画面には、「Supabase Preview」というチェックが出ていて、無料プロジェクトでは**「skipped(スキップ)」**でした。
「skipped」は失敗ではなく、やっていないという意味です。プレビュー用の環境を作る機能(7章)がオフだからです。ただ、これが後で効いてきます。
Merge pull request を押してマージしました。しばらくしてから Database → Migrations を見ると……
2枚目だけでなく、1枚目も一緒に適用されていました。連携は「まだ適用されていないマイグレーションを、ファイル名の順にすべて」反映するようです。
もう1つ、気づいたことがあります。「INSERTED AT」の時刻が 00:02:00 のように、ファイル名の数字そのままでした。実際に反映したのはもっと後なので、この列は「反映した時刻」ではなくファイル名から作られた時刻のようです(私の推測で、公式に確認したわけではありません)。
4. わざと壊してみる:失敗はどこに出る?
次に、すでにある列をもう一度足すという、わざと壊したSQLをマージしました。
alter table public.notes add column author text;
無料プロジェクトでは、プルリクの時点で止めるものはありませんでした(1本目と同じ設定なので、チェックは「skipped」のはずです。ただ、この壊れたプルリクは作ってすぐマージしたので、その画面そのものは見ていません)。マージしたあと、mainのコミットのチェックが**赤(失敗)**になりました。
エラー文は、column "author" of relation "notes" already exists。何が悪いかは、ちゃんと読めます。
でも、ダッシュボード側には何も出ません。プロジェクトの概要の「LAST MIGRATION」は、前の変更のままで、ステータスは「Healthy」です。
つまり、無料プロジェクトでは、失敗に気づく場所はGitHubだけで、しかもマージのあとです。「本番は壊れていない(反映されていないだけ)」ですが、気づかないと、変更が入っていないことに誰も気づかないかもしれません。
5. 1つ壊れると、後ろも全部止まる
もう1つ、試しました。壊れたファイルを残したまま、正しいマイグレーション(インデックスを作るだけ)をマージしたのです。
結果は失敗。しかも、さっきと同じ古いエラーでした。
順番に実行するので、詰まったファイルより後ろは、正しくても進めません。
直し方は、壊れたファイルを直すだけでした。そのファイルはまだ一度も適用されていなかったので、同じファイルをその場で書き換えて(add column if not exists に変更)マージしました。すると、残りも一緒に適用されました。
注意:すでに適用されたファイルを書き換えるのは別の話です。適用済みのファイルは直さず、新しいファイルで直すのが基本のはずですが、これは私は試していません。
6. Edge Functionも運ばれる/設定は運ばれない
ドキュメントには、運ばれるのは、マイグレーションと、config.tomlに書いたEdge Functionなどと書かれていました。本当か確かめました。
Edge Function:supabase/functions/applied-migrations/index.ts を置き、config.tomlに次を足してマージしました。
[functions.applied-migrations]
verify_jwt = false
ダッシュボードのEdge Functionsに、関数が自動で現れました。
呼び出してみると、鍵なしでは関数自身のエラー文が返ったので、verify_jwt = falseも反映されていました。(※ここでverify_jwt = falseにしたのは、デモのためです。注意点はサンプルのREADMEに書きました。)
設定:同じプルリクで、config.tomlの [api] max_rows を 1000 から 7 に変えてみました。ダッシュボードのData API設定を見ると……
1000のままでした。ダッシュボードで決める設定は、この連携では運ばれないようです。「ファイルに書けば全部運ばれる」と思っていると、ここでつまずきそうです。
7. プルリクの時点で失敗を見たい:プレビューブランチ(有料)
4章で、無料プロジェクトではマージの後にしか失敗が分からないと書きました。マージの前に分かる方法が、プレビューブランチです。
仕組みは、プルリクごとに、使い捨てのお試し環境(本物のデータベースの複製。データは入らない)を自動で作り、そこに新しいマイグレーションを流して試す、というものです。Proプラン以上で使えます。
ダッシュボードのつなぎ方は同じで、Proのプロジェクトだと、フォームに最初からブランチ機能がオンで出ます。
注意書きに、**「ブランチのコンピュート(計算)はスペンドキャップ(使いすぎ防止の上限)の対象外」**とあります。上限が効かないので、使い終わったら消すことが大事です。
このProプロジェクトで、**壊れたマイグレーションのプルリク(マージしない)**を作りました。結果は、マージの前、プルリクの画面に出ました。
- ボットのコメントに、タスクの表が出ます。Configurationsは✅、Migrationsが❌、SeedingとEdge Functionsは一時停止(⏸)でした。
- 同じエラー文(今度は存在しないテーブル)が、マージの前に読めました。
- コメントには「新しいマイグレーションのファイルだけが流される」という趣旨の注意書きもありました。
無料プロジェクトとの違いは、失敗に気づくのが、本番の前か後かです。これが私が一番「なるほど」と思った点です。
8. 費用のまとめ
| 項目 | 費用 | 私の実行 |
|---|---|---|
| GitHub連携(無料プロジェクト) | $0(ドキュメントでは全プランで使える) | 実測:課金なし |
| プレビューブランチ | ドキュメントでは固定費なし、使った分だけ。Microのブランチは1時間あたり$0.01344から、スペンドキャップの対象外(2026-10-31確認) | ブランチは約2分 |
| 追加のMicroプロジェクト(Pro) | 同じ単価。月約$10 | 約15分で削除 |
私の短時間の検証は、ドキュメントの単価で計算すると数セント以下です。ただし、請求書では確認していません。1時間未満が切り上げかどうかも確認していません。長く動かしっぱなしにすると月$10ずつ増えるので、試したら消しましょう。
9. まだ試していないこと/穴(正直に)
-
ルートB:GitHub Actionsで、SQLの検査(
supabase db lint)、テスト(supabase test db)、本番に出す前の承認まで組む標準的な構成。私は動かしていません。ネットで見かけた例を読んだだけで、たとえば「検査の前にローカルのデータベースを起動する必要があるのでは」など、動かしてみないと分からない点があります。 - 複数の文が入ったマイグレーションが途中で失敗したとき、一部だけ反映されるかどうか(私の壊したファイルはどれも1文でした)。
- プライベートのリポジトリ、
supabaseフォルダが深い場所にある場合、プルリクが同時に何本もある場合。 - 無料プロジェクトで、プルリクの時点で失敗を見る方法は、見つけられませんでした(ブランチ機能が必要でした)。
10. 後片付け
連携を無効にし(Settings → Integrations → Disable integration)、テーブルとマイグレーションの記録、Edge Functionを消し、使い捨てのProプロジェクトも削除しました。GitHubに入れたSupabaseアプリは、不要なら github.com/settings/installations から外せます。
まとめ
- SQLファイルをマージするだけで、データベースに反映できた(ダッシュボードの設定だけ。費用は無料)。
- 連携は未適用のものを順番にすべて反映する。1つ壊れると後ろも止まる。
- 無料だと、失敗に気づくのはマージの後。ダッシュボードには出ず、GitHubだけに出る。
- 運ばれるのは、マイグレーションとEdge Function。ダッシュボードの設定は運ばれない。
- マージの前に失敗を見るには、有料のプレビューブランチ。ただし上限が効かないので、消し忘れに注意。
次は、この状況を一目で見られるボードにしてみました(続きの記事)。












