🌱

SupabaseのローカルでKnex.jsを使ってマイグレーション

2023/02/11に公開約4,700字

ローカルのSupabaseでマイグレーションをするには

Supabaseはローカル開発環境を用意してくれており、こちらを利用させてもらうことで開発がスムーズにできます。Supabaseでマイグレーションするには以下のような方法が考えられます。

それぞれの方法には一長一短があって、生SQLを使うパターンの場合、seedを入れるのにPL/pgSQLを少し知っていたほうが良いケースがあります。Prismaの場合は構文を習いGitHubのディスカッションについて調べる必要が出てくるかもしれません。Knex.jsも構文を習う必要があります。フロントエンドのPrismaに詳しくない人からすると現時点でとっつきやすいのはKnex.jsだと思うので、こちらの方法を紹介させていただきます。

ローカルにSupabaseをインストールする

ドキュメントが用意されています。
https://supabase.com/docs/guides/cli/local-development
インストールするにはかなり通信量が多いのでご注意ください。
うまくいけば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が生成したら次のようにファイルの中を変更します。

knexfile.js
// 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フォルダの中のファイルを開き、次の通り編集します。

migrations/...js
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テーブルになります。↓詳細はこちら。
https://supabase.com/docs/guides/auth/managing-user-data

次のコマンドを実行することで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
seeds/development/seeds.js
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

ログインするとコメントできます