1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SupabaseのLocal Storageが、DB初期化で吹き飛んじゃうので、復元機能を作成してみた

Posted at

Supabaseによるローカル開発(概要)

Flutterでアプリケーションを作成を行っており、データベースとストレージしてSupabaseを使っています。

現在(2025/04/01)Supabaseは、
LAUNCHWEEKで新機能が発表されている最中です。(公式サイト)

Supabaseにはローカル開発環境(※1)があるので、
常に実際のプロジェクト(クラウド上)にアクセスする必要はありません。

ローカルで立ち上げたDockerコンテナを用いて、データベースからのデータ取得やストレージからのファイル取得を行えます。

ローカルだけで作業を進められるのは、非常にありがたいことなんですが、
データベースとStorageと組み合わせると、ちょっと困ったこと(改善できそうなこと)がでてきました。

Supabase Storage
文字通りですが、ストレージサービスです。

AWSのs3やAzureBlob Storageなど、ストレージサービスを利用されたことがある方は、イメージしやすいかと思います。

以下公式の情報をご確認ください。

※1:ローカル開発環境
Dockerコンテナを使って、データベースやストレージを用いたローカル開発ができます。

公式ドキュメント

以下は私の方で先日公開した記事です。こちらもよろしければご覧ください!

この先を読み進める前に

プログラミング初学者向けではありません。以下の内容についてご存知ではない場合、
読み進めるのが少し大変かもしれません。

  • Dockerコンテナ
  • データベース接続とSQL(Postgres)の基本(テーブル作成やデータ登録)
  • S3などのクラウドストレージ操作
  • javascriptなど何らかの言語の基本的な実装

DB初期化でバケット自体が削除される

話を戻しますが、
困ったのは、データベースの初期化コマンドを実行したときです。

npx supabase db reset

resetという単語が使われているので、「初期化」としていますが、正確には再生成をするためのコマンドです。
実行すると、以下の処理を行います。

  1. データベース自体を削除
  2. マイグレーションファイル(SQLファイル)に沿って、テーブル作成やRLS定義を実施
  3. seed.sqlに定義したSQLで各テーブルへデータを投入

しかしStorageに関しては、
バケットにアップロードしたファイル、バケット、設定したバケットポリシー(※2)
これら全てが消えます。

以下画像は、初期化コマンドを実行前のローカル環境のストレージの中身です。

コマンドを実行すると、
バケット・アップロードファイル(青枠で囲った箇所)が消えます。
image.png

ポリシー(青枠で囲った箇所)も同様に消えます。
image.png

DBの初期化、ってそんなにするの?となるかと思いますが、
それなりの回数は実行するかと思います。

DB側でテーブルを追加したり、カラムを追加・削除したりした後、これまでとの差分情報を取得します。(マイグレーションファイルの自動生成)

npx supabase db diff --use-migra -f sample

このマイグレーションファイル作成コマンド実行後、
ターミナルに「db resetを実行して、問題がないか確認をしてください。」といった旨のメッセージが表示されます。

image.png

DBを初期化して、
問題がないか(差分が正しく反映されるか)確認を行います。

DB初期化の度に以下作業を行うのは大変です。
いずれも増えるほど、全体の作業量が増えてしまいます。

  • バケット作成
  • ファイルアップロード
  • バケットポリシーの適用(※2)

※2:バケットポリシーは、以下をご覧下さい。

ネットで調べてみると、明確な方法は見当たらず、
スクリプトを作成してもらう対応になる、といったコメントも見つけました。

config.toml(設定ファイル)にも、設定できそうな箇所はありませんでした。
(ファイルの場所は後述のフォルダ構成で確認できます。)


[storage]
enabled = true
# The maximum file size allowed (e.g. "5MB", "500KB").
file_size_limit = "50MiB"

# Image transformation API is available to Supabase Pro plan.
# [storage.image_transformation]
# enabled = truew

# Uncomment to configure local storage buckets
# [storage.buckets.images]
# public = false
# file_size_limit = "50MiB"
# allowed_mime_types = ["image/png", "image/jpeg"]
# objects_path = "./images"

ということなので、スクリプトを組んでみることにしました。
やってみると、
思っていたよりは短時間で実現することができたので、その内容を共有します!

ゴールは、
DBの初期化時に、バケット・バケットの中身・バケットポリシーを復元できるようにすることです。

スクリプトを組む前に

必要な情報

以下コマンドを実行してコンテナを起動します。

npx supabase start

コマンド実行後、以下内容がターミナルに出力されます。この情報を利用してスクリプト作成を進めます。
S3という文字が数か所ありますが、AWSのS3のことです。

    GraphQL URL: http://127.0.0.1:54321/graphql/v1
  S3 Storage URL: http://127.0.0.1:54321/storage/v1/s3
          DB URL: postgresql://postgres:postgres@127.0.0.1:54322/postgres
      Studio URL: http://127.0.0.1:54323
    Inbucket URL: http://127.0.0.1:54324
      JWT secret: super-secret-jwt-token-with-at-least-32-characters-long
        anon key: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ8.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ8.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0
service_role key: eyJhbCgiOiJIUzI1NiLsInR5cCI1IkpKECJ1.eyJpc1MiOiJzdIQhGmFzZS1kKW1vIiwicm1sZOS1InNlcnZpY1Viwm1sZSIsImV1cCI1MTk4MzgxMjk5Nn0.EGKF31RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU
   S3 Access Key: 929003a08b11bf1b5ff111c111f1d11c
   S3 Secret Key: 123456e4652dd093b7a15c58ae0d2d34bd487cc0ea1111aed6eda11111111111
       S3 Region: local

anon keyservice_role keyは以下をご確認ください。

キーやURL情報の扱い
ローカルでのみ利用可能な情報なので、特に隠さずに表示しています。(値自体も手を入れています)
実際の環境における各値の管理にはご注意ください。

必要な手順

ざっくりです。詳細は後述の通りです。

  • 現在ストレージ上(バケット)にある各ファイルオブジェクトを取得
  • バックアップフォルダへ配置
  • データベースの初期化
  • バケットの再作成
  • バックアップフォルダからバケットへファイルアップロード
  • バケットポリシーの適用

これらの処理を、3つのスクリプトに分けて進めることにしました。
javascriptで書きます。

図で示すと、以下の通りです。
左側に記載したファイル名で各スクリプトを作成します。(xxx.jsの部分)

image.png

スクリプト作成と動作確認

フォルダ構成

先に最終的なフォルダの構成の情報を載せておきます。
javascriptを触ったことがある方は大きく疑問に感じる点はないと思います。

image.png

  • scriptsフォルダ
    • 環境変数を記述した.envファイル(sampleはgit管理用)
    • 復元処理を実装したスクリプトファイル
    • バケットポリシー用のSQLファイル
  • supabaseフォルダ
    • npx supabase init実行時に自動生成されたフォルダで、画像にある通りマイグレーションファイルやデータ投入のためのseed.sqlなどを配置
  • package.json
     - 必要なライブラリやスクリプトを記載(復元処理のためのコマンドもここで定義)

各ファイルの情報を記事の最後に載せました。
こちらからも確認いただけます。

必要になったライブラリ(package.json参照)

  • supabase:supabaseのDB操作で必要なので、Storage関係の作業前からインストール済
  • @aws-sdk/client-s3:javascriptでS3操作する際に必要
  • npm-run-all:複数スクリプトを一度で実行する場合に必要
  • @supabase/supabase-js:Supabaseのjavascript用SDK
  • mime-types:バケット作成時に、アップロードを許可するMimeTypes(※3)を指定
  • pg:SupabaseはPostgresで動いており、DB接続で必要

※3:MimeTypesが気になる方は以下をご確認ください。

バックアップ処理(backupStorage.js

  1. S3への接続
  2. 対象バケットからオブジェクト取得
  3. 2で取得したオブジェクトをローカルのフォルダに保存

(ここでデータベースの初期化が実行され、バケットも削除)

復元処理(restoreStorage.js

  1. バケット作成
    1. public設定はfalse(※4)
    2. アップロード可能なMimeTypesを['image/png']で設定(他は特に必要なし)
    3. 最大サイズを10MBに設定
  2. バックアップにある各ファイルをアップロード
    1. バックアップ時にフォルダ構成もバケットの状態を復元しています。

※4:publicprivateの違いは以下参照

ポリシー適用処理(applyBucketPolicy.js

  1. ローカルのデータベースに接続
  2. javascriptファイルと同階層に配置したSQLファイルを読み込み、ファイル内容をバケットに対して適用

スクリプト実行

以下のように、package.jsonのscriptで記載した通りです。

package.json
{
  "devDependencies": {},
  "scripts": {
    "start": "supabase start",
    "stop": "supabase stop",
    "db:reset": "run-s db:backup:storage db:do:reset db:restore:storage db:restore:policy",
    "db:do:reset": "supabase db reset",
    "db:backup:storage": "node scripts/backupStorage.js",
    "db:restore:storage": "node scripts/restoreStorage.js",
    "db:restore:policy": "node scripts/applyBucketPolicy.js"
  },
  "dependencies": {}
}

以下コマンドを実行して、復元まで行うことができました。

npm run db:reset

動作確認

以下はコマンドを実行したときのマネジメントコンソールの動画です。
実際は1分ほどかかりますが、中間はカットしました。

qiita_supabase_storage_restore.gif

はじまってすぐに、動画右上にエラーっぽいダイアログが表示されます。コンテナの初期化が開始され、アクセスできない状態に入ります。

その後、下部中央あたりに黄色のダイアログで、バケットにアクセスできません、と表示されます。

(ここで実際は30秒ほど待ちます)

最後にリロードをして、バケットに画像がアップロードされていることを確認できます。
画像を確認できたので、復元できていることになります。

起こりうるケースに対応したスクリプトへ

DB初期化時のデータ投入SQLでエラーが発生してしまった

図で書くと以下の通りです。以降は同じです。
image.png

発生した場合、バックアップは存在し、バケットが存在しない状態になります。
このまま再度処理を実行すると、バケットが存在しないため、その段階でエラーが発生します。

バケットが存在しないエラーをcatchし、既に作成済のバックアップを適用するか、ユーザ入力を求める形にしました。
yを入力した場合は、この処理は正常終了扱いとして、DB初期化に進み、
nとした場合は、エラーで終了するようにしました。

間違ってストレージのファイルを消してしまった

図で言えば、以下処理だけ実行します。
image.png

バックアップがない初回のタイミングでやってしまった場合は、どうしようもありませんが、
一度バックアップを作成済であれば、復元可能です。

package.jsonには記載してありますが、
以下コマンドを実行すれば復元処理だけ実行可能です。

npm run db:restore

まとめ

今回はSupabase DatabaseとStorage、
両方を用いたローカル開発を進めやすくする仕組みを作ることができました。

作業中は適宜、
ClaudeやDeepSeekなどに助けてもらいながら進めました。
方向性が大きくずれることが少ないので、思っていたよりも短い時間で進めることができました。

Supabaseをこの後もさわる機会が多いので、
新たにまとめられそうな情報があれば、まとめたいと思います。

実装詳細

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?