Drizzle ORMでSupabaseのMigrationを書く個人的ベストプラクティス

この記事はSupabase Advent Calendar 10日目の記事です。18日も遅れてすみません、、、

SupabaseのMigrationをDrizzle ORMで書く

SupabaseのMigrationは公式機能として存在していて、SQLファイルを自動生成してそこに書いていくというものになりますが、TypeScriptとしてORMを記述することができずちょっと不便です。 そこで、Drizzle KitのMigration機能を利用することでDrizzle ORMからもSupabaseのSDKからも使えるコードを生成でき、これが割とよかったのでやり方を解説します。

SupabaseのSDKはhttp経由でDBアクセスを行うため、Cloudflare WorkersのTCP機能などが活かせない問題があり、Drizzle ORMをCF Workersでは使いたいと思っている方には参考になるかと思います。

Supabaseの設定をする

supabase initコマンドでsupabaseディレクトリを作成します。ここにmigrationsフォルダが来るようになります。

Drizzle Kitを使ってmigrationを作成する

まず、schema.tsなどとしてスキーマを定義します。

import {pgTable, uuid, text, timestamp} from "drizzle-orm/pg-core"

export const users = pgTable("users", {
    id: uuid("id").primaryKey(),
    userName: text("user_name").notNull(),
    createdAt: timestamp("created_at", {mode: "date"}).notNull().defaultNow(),
    updatedAt: timestamp("updated_at", {mode: "date"}).notNull().defaultNow(),
})

また、ここでdrizzle kitの設定のためにdrizzle.config.tsを定義します。

import type { Config } from "drizzle-kit"

export default {
    schema: "./src/schema/**/*",
    out: "./supabase/migrations",
    driver: "pg",
} satisfies Config

ここでoutsupabase/migrationsを指定しておくことで、Supabase側から認識してもらうことが可能です。

ここまできたらあとはmigrationを作るだけです。

$ drizzle-kit

これでsupabase/migrationsファイルにmigrationが作成されました。SupabaseとDrizzle Kitのmigrationファイルの名前構造が、Drizzleは自動の名前+連番なのに対しSupabaseはユーザー入力の名前+timestampであるので、構造が同じなため読み込みに成功します。

また、ここからSupabase SDK用の型をsupabase gen types typescriptコマンドで生成すればDrizzleとSupabaseどちらも対応することができます。

質問: RLSとか設定したいんですけど。

RLSの設定はまだDrizzle ORMではできないため、一回drizzle-kitコマンドでSQLファイルを生成した後にそのファイルに追記する形でRLSを記述することができます。

ALTER TABLE "public"."users" ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Enable modify for users based on user_id" ON "public"."users"
AS PERMISSIVE
TO public
USING (auth.uid() = id)
WITH CHECK (auth.uid() = id);

結論

Drizzle ORMの機能を活かしながら、RLSまで設定できるためこの構成が気に入っています。また、これをpnpm workspaceなどで別にして管理することで型を分離することができ、綺麗なWorkspaceを作ることができるのでおすすめです。

アイキャッチは無料10連で引いた銀狼です。フォフォ欲しかった...

それでは、さようなら。