SupabaseのローカルでKnex.jsを使ってマイグレーション
ローカルのSupabaseでマイグレーションをするには
Supabaseはローカル開発環境を用意してくれており、こちらを利用させてもらうことで開発がスムーズにできます。Supabaseでマイグレーションするには以下のような方法が考えられます。
- 生SQLおよびPL/pgSQL
- Prisma(ORM)
- Knex.js(SQLクエリビルダー)
それぞれの方法には一長一短があって、生SQLを使うパターンの場合、seedを入れるのにPL/pgSQLを少し知っていたほうが良いケースがあります。Prismaの場合は構文を習いGitHubのディスカッションについて調べる必要が出てくるかもしれません。Knex.jsも構文を習う必要があります。フロントエンドのPrismaに詳しくない人からすると現時点でとっつきやすいのはKnex.jsだと思うので、こちらの方法を紹介させていただきます。
ローカルにSupabaseをインストールする
ドキュメントが用意されています。
うまくいけばhttp://localhost:54323/にアクセスするとこんな画面が出るはずです。
マイグレーションの手順
前提として、認証機能をつけたいケースでの手順を記載します。認証機能をつけるには、色々気にしないといけないことが多いためです。そうでない場合は適宜手順を変えてください。前項で生成したsupabaseフォルダに移動し、package.jsonを生成します。
npm init -y
pgとknexをsupabaseフォルダにインストールします。
npm install -g knex
npm install pg
knexはグローバルにインストールします。
knexfile.jsを生成します。
knex init
knexfileが生成したら次のようにファイルの中を変更します。
// Update with your config settings.
/**
* @type { Object.<string, import("knex").Knex.Config> }
*/
module.exports = {
development: {
client: "pg",
connection: "postgresql://postgres:postgres@localhost:54322/postgres",
pool: {
min: 2,
max: 10,
},
migrations: {
directory: __dirname + "/migrations",
},
seeds: {
directory: __dirname + "/seeds/development",
},
},
};
postgresql://postgres:postgres@localhost:54322/postgresの部分がローカルのpostgresのURLですね。
次に、マイグレーション用のファイルを生成します。
npx knex migrate:make migration_name
生成されたmigrationsフォルダの中のファイルを開き、次の通り編集します。
exports.up = function (knex) {
return knex.schema.createTable("profiles", (table) => {
table
.uuid("id")
.references("id")
.inTable("auth.users")
.primary()
.onDelete("CASCADE");
table.string("name", 18).notNullable;
});
};
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function (knex) {
return knex.schema.dropTable("profiles");
};
このファイルの説明をしますと、auth.usersというユーザーの機密情報が入ったテーブルには、セキュリティ上の理由により一般ユーザーはアクセスできません。アクセスできないということは、自分の名前やハンドルネーム、アイコンなどの情報を取得できないということになります(メールアドレスなどの一部情報はauth.getUserから取得できます)。なので代わりに一般ユーザーでもユーザー情報にアクセスできるようにしたのが、このファイルで作成しているprofilesテーブルになります。↓詳細はこちら。
次のコマンドを実行することでmigrationのupが実行されます。
knex migrate:up
http://localhost:54323/にアクセスすると以下のとおり、profilesテーブルが作成されます。
triggerを入れる
auth.usersとprofilesテーブルを紐付けるために、triggerをデータベースに登録しておきます。auth.usersテーブルにデータが入った=新規ユーザー登録したときにprofilesにデータが入るようにします。
seed.sqlに次のコードを記述します。
-- inserts a row into public.profiles
create function public.handle_new_user()
returns trigger
language plpgsql
security definer set search_path = public
as $$
begin
insert into public.profiles (id, name)
values (new.id, new.raw_user_meta_data ->>'name');
return new;
end;
$$;
-- trigger the function every time a user is created
create trigger on_auth_user_created
after insert on auth.users
for each row execute procedure public.handle_new_user();
一旦、次のコマンドを実行して仕切り直します。supabase stopを実行するとデータベースが初期化されます。
supabase stop
supabase start
knex migrate:up
supabase start時にseed.sqlが実行されます。うまくいってれば次のようにon_auth_user_createdトリガーが生成されているはずです。
seedを入れる
次のコマンドを実行してseedsファイルを生成します。
knex seed:make seeds
const crypto = require("crypto");
const uuid1 = crypto.randomUUID();
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.seed = async function (knex) {
// Deletes ALL existing entries
await knex("auth.users").del();
await knex("auth.users").insert({
id: uuid1,
raw_user_meta_data: JSON.stringify({ name: "hoge" }),
});
};
uuidを生成して、auth.usersテーブルとprofilesテーブルにseedを入れています。
これでデータが入りました。
-
auth.usersテーブル
-
profilesテーブル
テーブルを追加していけば、Supabaseでフロントエンドの開発を進められるようになると思います。
Knex.jsはPrismaと比べるとできることが少なく、modelからの型生成はできません。しかし、Supabase CLIのほうでテーブルからの型生成はカバーできているため、役割の衝突が起こらず相性が良いと思います。なお、クライアント側でのCRUDには@supabase/supabase-jsを用い、Knexは使いません。これはSQL Injectionを避けるためにクライアント側でのSQLが制御されているためです。ではでは。
Discussion