React Router × Cloudflare Workers × Supabase で Drizzle ORM を試す
こんにちは。Gaji-Labo の村上です。
React Router × Cloudflare Workers × Supabase で個人開発を進めるなか、ORM を導入したいと考えました。
そこで、Drizzle ORM を試してみたところ、相性が良さそうに感じたため紹介します!
この構成は、なるべくコストをかけずに開発したい方におすすめです!
Cloudflare Workers は無料枠があり、Supabase も無料プランで十分な機能を提供しているため、コストを抑えながらアプリを構築できます。
本記事では、Drizzle ORM の導入をメインに、ローカル環境を構築し初期データ (seed) を Supabase に投入するところまで紹介します。
React Router:ver 7.1.5
Supabase CLI:ver 2.9.6
Drizzle ORM:ver 0.39.1
相性がいいと感じたポイント
Supabase とのマイグレーション管理がスムーズ
Supabase CLI にはスキーマ管理の仕組みがないため、通常は SQL ファイルで手動管理する必要があります。
しかし、Drizzle ORM を使えば、スキーマを TypeScript で定義し、マイグレーションファイルを Drizzle で生成 → Supabase CLI で適用 という流れで管理できます。
Cloudflare Workers でも動作する
Cloudflare Workers 環境で動作しない ORM もありますが、Drizzle ORM は Cloudflare Workers でも動作 します。
また、Drizzle はサイズが軽量で Prisma 導入時よりも早く動作するようです。
参考:https://zenn.dev/saneatsu/scraps/7be2fcc923be42
RLS に対応している
Prisma では公式にサポートされていないですが、Drizzle は公式にサポートされていて便利です。
参考:https://orm.drizzle.team/docs/rls
最終的なディレクトリ構成
/my-project
├── app
│ ├── db
│ │ ├── client.server.ts
│ │ ├── schema.ts
│ │ └── seeds.ts
│ ├── routes
│ │ ├── api.users.get.tsx
│ │ └── home.tsx
│ ├── ...
│ └── routes.ts
├── public
├── supabase
├── workers
├── .dev.vars
├── .gitignore
├── drizzle.config.ts
├── package.lock.json
├── package.json
├── ...
├── vite.config.ts
├── worker-configuration.d.ts
└── wrangler.toml
React Router プロジェクト作成
React Router の公式ドキュメントに沿ってプロジェクトを作成していきます。
今回は cloudflare テンプレートを利用します。
npx create-react-router@latest --template remix-run/react-router-templates/cloudflare
Supabase CLI を導入
Supabase Local Development & CLIに則って進めます。npx supabase start
実行後にターミナルに表示される、Supabase の DB URL は後ほど使うのでコピーしておきます。
Drizzle ORM を導入
関連パッケージをインストールし、初期データ投入まで行います。
関連パッケージをインストール
npm i drizzle-orm postgres drizzle-seed dotenv
npm i -D drizzle-kit dotenv-cli tsx pg
環境変数を用意
wrangler を使って開発をしているので、ローカルの環境変数を .dev.vars ファイルを作成して定義します。
.dev.vars ファイル作成後は、.gitignore に追加しておきます。
// supabase の DB URL
DATABASE_URL="postgresql:xxxxxxxxx"
次に、package.json の scriptsに"typegen": "wrangler types"
scripts を追加します。
npm run typegen を実行すると .dev.vars から型定義ファイル(worker-configuration.d.ts)を生成され、DATABASE_URL の型定義が記載されているのが確認できます。
テーブルスキーマを定義
app/db/schema.ts ファイルを作成し、users テーブルを定義します。
(ここでは仮に users テーブルとしていますが、適宜変更してください。)
import { pgTable, serial, text, varchar } from "drizzle-orm/pg-core";
export const users = pgTable('users', {
id: serial('id').primaryKey(),
fullName: text('full_name'),
phone: varchar('phone', { length: 256 }),
});
マイグレーションの設定
drizzle.config.ts ファイルに drizzle-kit の設定を定義します。
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
out: './supabase/migrations',
schema: './app/db/schema.ts',
dialect: 'postgresql',
});
drizzle で作成したマイグレーションファイルを supabase ディレクトリに出力するのがポイントです。
supabase migration up コマンドでマイグレーションを Supabase に適用できるようになることで、ローカルの変更内容を本番環境にも簡単にマイグレーションしやすくなります。
次に、package.json にマイグレーションファイルを生成する db:generate と、マイグレーションを Supabase に適用する db:migrate script を追加します。
"scripts": {
"db:generate": "drizzle-kit generate",
"db:migrate": "supabase migration up"
}
マイグレーションの実行
npm run db:generate
npm run db:migrate
マイグレーションを実行し、http://localhost:54323/project/default/editor に users テーブルが追加されていれば OK です。
シードデータの投入
app/db/seeds.ts ファイルを作成します。
import { seed } from 'drizzle-seed';
import * as schema from './schema';
import dotenv from 'dotenv';
import { drizzle } from 'drizzle-orm/node-postgres';
dotenv.config();
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, schema);
}
main();
次に、package.json に db:init script を追加します。
seeds.ts では process.env.DATABASE_URL としているため .env が必要になります。
ですが、seed 作成のためだけに .env を管理するのは避けたいので、環境変数を .dev.vars から読み込めるように dotenv を利用します。
"scripts": {
"db:init": "dotenv -e .dev.vars -- tsx ./app/db/seeds"
}
npm run db:init 実行後、http://localhost:54323/project/default/editor の users テーブルを確認して、10個の初期データが投入されていれば OK です。
初期データを取得して console に出力してみる
Supabase に登録されたデータを Cloudflare Workers 経由で取得し、フロントエンドで console.log してみます。
Drizzle のクライアントを作成
Supabase のデータベースと接続するためのクライアントを作成します。
app/db/client.server.ts を作成し、Drizzle を利用して PostgreSQL に接続できるようにします。
import postgres from 'postgres'
import { drizzle } from 'drizzle-orm/postgres-js'
export const db = (env: { DATABASE_URL: string }) => {
const client = postgres(env.DATABASE_URL, { prepare: false })
return drizzle(client)
}
API ルートを作成
次に、Cloudflare Workers で Supabase の users テーブルからデータを取得する API エンドポイントを作成します。
app/routes/api.users.get.tsx を作成し、データを取得して返します。
import type { Route } from "./+types/api.users.get";
import { db } from '../db/client.server';
import { users } from '../db/schema';
export async function loader({ context }: Route.LoaderArgs) {
const dbClient = db(context.cloudflare.env);
const usersTable = await dbClient.select().from(users);
return { users: usersTable };
}
これで http://localhost:5173/api/users/get にアクセスすると、Supabase の users テーブルから取得したデータが返るようになります。
API ルートを React Router に追加
API ルートを認識させるために、app/routes.ts に api/users/get を追加します。
import { type RouteConfig, index, route } from "@react-router/dev/routes";
export default [
index("routes/home.tsx"),
route("api/users/get", "routes/api.users.get.tsx"),
] satisfies RouteConfig;
フロントエンドでデータを取得し console に出力
最後に、フロントエンド (home.tsx) で users データを取得し、console.log で確認します。
import type { Route } from "./+types/home";
import { Welcome } from "../welcome/welcome";
export function meta({}: Route.MetaArgs) {
return [
{ title: "New React Router App" },
{ name: "description", content: "Welcome to React Router!" },
];
}
export async function loader({ context }: Route.LoaderArgs) {
const message = context.cloudflare.env.VALUE_FROM_CLOUDFLARE ;
const res = await fetch("http://localhost:5173/api/users/get");
const users = await res.json();
return { message, users };
}
export default function Home({ loaderData }: Route.ComponentProps) {
const users = loaderData.users;
console.log(users);
return <Welcome message={loaderData.message} />;
}
http://localhost:5173/ で検証ツールからコンソールを確認し、users が表示されていれば OK です。
最後に
Supabase CLI にはスキーマを設定するファイルがないため、Drizzle を使って TypeScript で定義できるのはとても便利でした。
型安全にデータベース操作を行え、簡単にスキーマ管理ができるのは Drizzle ORM の大きな魅力です。
Supabase との組み合わせによって、スムーズにデータベースの構築・管理が可能になります。
Gaji-Labo では、React Router をはじめとしたフロントエンド技術を活用し、スタートアップのプロダクト開発を支援していますので、興味のある方はお気軽にカジュアル面談をお申し込みください!
Gaji-Labo フロントエンドエンジニア向けご案内資料
Gaji-Labo は新規事業やサービス開発に取り組む、事業会社・スタートアップへの支援を行っています。
弊社では、Next.js を用いた Web アプリケーションのフロントエンド開発をリードするフロントエンドエンジニアを募集しています!さまざまなプロダクトやチームに関わりながら、一緒に成長を体験しませんか?
もちろん、一緒にお仕事をしてくださるパートナーさんも随時募集中です。まずはお気軽に声をかけてください!