Remix + Google Auth + Cloudflare Pages + Drizzle + D1 のテンプレートを作った
Remix で SSR するアプリが Cloudflare Pages 上で動きます。Google 認証付です。
本番で動かすならデータベースは別にしたいのですが、エッジサーバー上で SSR できる。安くて早くて美味い。そんな組み合わせです。
シンプルな構成にするために、ORMはDrizzle ORMを使っています。Prismaもエッジ環境での実行にベータ版対応しましたが、サイズがデカくなり過ぎるので見送っています。
デモサイト
リポジトリ
新規作成する場合
pnpx create-remix@latest --template ryokryok/remix-cloudflare-pages-d1-drizzle
工夫した点
認証
既に先行で作成された例を参考しました。
現在は型安全に書きやすくなっています。
Config から生成した型を使うようにする
Cloudflare のサービスをバインディングを型安全にするために、wrangler types
で worker-configuration.d.ts
を生成できます。
この型はload-context.ts
で読み込まれます。
import { type PlatformProxy } from "wrangler";
type Cloudflare = Omit<PlatformProxy<Env>, "dispose">;
declare module "@remix-run/cloudflare" {
interface AppLoadContext {
cloudflare: Cloudflare;
}
}
これによって入力時に型補完が効くようになります。
同一ページから複数 action がある場合
Remixでは表示するComponent, 表示時にデータ取得や読み込みをするLoader, POST時に実行するActionが定義できます。
同一ページ内で複数Actionを定義する場合はどうするの?というIssueがありました。
個人的にデータの追加と削除は別ルーティングで行いたいのと、機能が少ないので1処理1ファイルにしても大丈夫だろうと判断しました。
Remixの独自タグのForm
はactionを実行する先を変更できるのでこれでPOST先を指定します。
<Form action="/posts/create" method={"POST"}>
<input
type="text"
name="post-body"
id="post-body"
/>
<button
type={"submit"}
>
Create Post
</button>
</Form>
対応するパスにはactionの処理を書きます。表示する必要がないのでComponentもLoaderも書きません。
最後に実行元へのredirectを返すようにします。
export const action = async ({ context, request }: ActionFunctionArgs) => {
const db = getDBClient(context.cloudflare.env.DB)
const formData = await request.formData()
const postBody = formData.get("post-body")?.toString()
// validation
if (postBody === undefined || postBody.length === 0) {
return new Response("Post body is empty", { status: 500 })
}
await db.insert(posts).values({ body: postBody?.toString()})
}
return redirect("/")
};
残っている課題
- テスト
- @cloudflare/vitest-pool-workers/config のテスト設定と Remix の設定を共存させたい
- これがあると
env.DB
みたいな方法で D1 にアクセスしてテストが書ける
- GitHub Workflow のデプロイ
- 現在は
wrangler.toml
の設定を反映させるためにローカルから build&deploy - Wrangler GitHub Action でいい感じにやりたい
- 現在は
Remixの所感
React版Ruby on Railsみたいな「設定より規約」みたいな思想を感じるからか、フレームワークとしてのマジカル度は低めでとっつきやすくて好きです。
Discussion