
Supabaseでマイグレーションとかロールバックとかやってみる
こんにちは。
今回はSupabaseという人気のあるBaaS(Backend as a Service)を試してみたいと思います。
● Supabaseとは?
ほぼFirebaseですが、Firebaseの代替として位置づけられており、大きな違いは、FirebaseがNoSQLデータベースを使用するのに対し、SupabaseではRDB(リレーショナルデータベース)であるPostgreSQLを使用する点です。
Firebaseを使用していて最も困難だった点は、NoSQLを使用することでした。
NoSQLではJOIN操作ができない、データ構造の変更にコストがかかるなどの課題があります。
特にビジネスプランの変更や仕様変更によるデータ構造の変更が頻繁に行われる開発フェーズでは、柔軟にデータ構造を変更できることや、リレーションを柔軟に扱えることが重要です。
そのため、RDB版のFirebaseがあればと考えていたところにSupabaseが登場しました。
1.Migrationしてみる
まず、円滑なデータベースの運用のために、Migrationに関連する仕組みを構築してみたいと考えています。
今回はReactからSupabaseを利用することを想定しているため、Node.jsから操作できる環境を構築する必要があります。
Node.jsでは、RailsでいうところのActiveRecordのようなORMが存在しないか探していました。
その中で、Prismaが候補に挙がりました。
● Prismaとは?
Prismaは「Next-generation Node.js and TypeScript ORM」とされており、Node.jsからデータベースにアクセスするためのORMです。モダンなアプローチで設計されています。
これでいいじゃん、完璧じゃん、と思っていましたがまさかの盲点がありました。
これまでの開発では、テーブルの新しい構造を反映するためにはMigrationを「Up」し、一つ前の構造に戻すためにはMigrationを「Down」することが一般的でした。
しかし、Prismaではスキーマを一つ前の状態に戻すことができないという盲点がありました。
「な、な、な、なんだってー」
公式の回答は「Seedを使用してデータをリセットする」というものですが、実際の運用ではこれは難しいと感じました。
Prismaでは他に適切な方法があるのか、または別のアプローチがあるのか知りたいと思います。
もしPrismaに関するより良い方法をご存知の方がいらっしゃれば、教えていただけると幸いです。
● 2021.12.14 追記
出典先でも議論されていますが、そもそも本番環境ではロールバックあまりしないよね?開発環境ならresetしてseedするのも自由だからmigration downなくてもいいよね 、ロールバックするのではなく、ロールフォワードを推奨だよ、という感じですかね。
本番で必要無くなったスキーマはロールバックするという方法ではなく、このスキーマを削除するという内容をmigration upしてねという感じでした。
2.Supabaseで本番用のDBをつくる
まずは、DBの環境として「development環境」と「production環境」を作成したいと考えています。
本番用のDB環境を作るのは簡単で、Supabaseの管理画面から「New Project」を選択します。
プロジェクトが作成されると、Production環境で使用するためのDBのアクセス情報も生成されるので、管理画面で確認しましょう。
3.Supabaseで開発用のDBをつくる
次に、development環境を作成します。
まずはローカルに適当なReactの開発環境を構築します。
開発環境が構築できたら、次にSupabaseをnpmでインストールします。
$ npm install -g supabase
これにより、Supabaseコマンドを実行できるようになります。
$ supabase init
✔ Port for Supabase URL: · 8000
✔ Port for PostgreSQL database: · 5432
✔ Port for email testing interface: · 9000
✔ Project initialized.
Supabase URL: http://localhost:8000
Supabase Key (anon, public): eyJ0eXAiOiJKV1Q...
Supabase Key (service_role, private): eyJ0eXAiOiJKV1Q...
Database URL: postgres://postgres:postgres@localhost:5432/postgres
Email testing interface URL: http://localhost:9000
Run supabase start to start local Supabase.
supabase initを実行すると、プロジェクト内にDocker環境が構築されます。
.supabase/docker/
構築されたDockerのコンテナ内にSupabaseが存在するので、それを起動してみましょう。
$ supabase start
これにより、SupabaseのAuthやDatabaseのサービスがローカルで利用できるようになります。
Supabaseを起動すると、コンテナ内でPostgreSQLが実行されるため、SQLクライアントやReactなどから接続することができます。

4.開発用と本番用を管理する
次に、ORMを導入して環境を効果的に管理したいと考えています。Prismaには不安要素があるため、安定して稼働する可能性が高いSequelizeを導入することにしました。
$ npm install --save sequelize-cli
$ npm install --save sequelize
導入が完了したら初期化します。
$ npx sequelize-cli init
そうすると、複数のディレクトリが生成されます。
● seeders - シードを定義するファイルを管理
● models - テーブルの構造を定義するファイルを管理
● migrations - マイグレーションファイルを管理
● config - DBの設定をするファイルを管理
configディレクトリにDBの設定を書きます。
{
// supbase initしたときのローカルのDB情報を入れる
"development": {
"username": "postgres",
"password": "postgres",
"database": "postgres",
"host": "localhost",
"dialect": "postgres"
},
// 今回は使わないがstaging環境などある場合に使用する
"test": {
"username": "root",
"password": null,
"database": "database_test",
"host": "127.0.0.1",
"dialect": "mysql"
},
// supabaseでNewProjectしたDB情報を入れる
"production": {
"username": // supabaseの管理画面から記載,
"password": // supabaseの管理画面から記載,
"database": // supabaseの管理画面から記載,
"host": // supabaseの管理画面から記載,
"dialect": // supabaseの管理画面から記載
}
}
developmentとproductionの設定に情報を記載します。
ここではUsersモデルの設定を行い、マイグレーションを実行してみたいと思います。
$ npx sequelize-cli migration:generate --name Users
Usersモデルの生成が完了し、modelsディレクトリにUsers.jsファイルが生成されます。
ここに追加したいテーブル構造を定義していきます。
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING
},
password: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Users');
}
};
記載が完了したら、最後にマイグレーションを実行するだけです。
$ npx sequelize-cli db:migrate --env development
環境ごとに実行する場合は、--envオプションを指定して実行すれば各環境に反映できます。
Sequelizeを使用すると、一つ前のスキーマにロールバックすることも可能なため、柔軟に開発を進めることができます。
Sequelizeのコマンドの詳細については、公式ドキュメントをご確認ください。
これにより、Supabaseから開発用と本番用のPostgreSQLを構築し、マイグレーションやロールバックを実行することで、実際の運用に近い形での開発環境が整いました。
このように簡単に構築できる点はFirebaseとほぼ同じで、非常に便利ですね。
Supabaseは様々な機能を提供してくれるBaaSであり、DBの中身はただのPostgreSQLですので、RDBの経験がある方は簡単に開発を進めることができるでしょう。
それでは。