🕌

Cloudflare Workers を活かしきるスタックを考えた(remix+d1 on pages-functions) + 残タスク

2023/05/07に公開

このスクラップ で試行錯誤していたまとめ。

最終形はここにアップロードした。

https://github.com/mizchi/remix-d1-bullets

docs の下に、このリポジトリを生成した手順、セットアップ方法、リリース方法を書いてある。

(remix-validated-form や vitest のテストの追加でもうちょっといじるとは思う)

なぜ

cloudflare-workers + d1 のポテンシャルは最強で、近い未来、開発者|個人開発者の銀の弾丸になると思っているのだが、それを活かす開発スタックが知られていない(要出典)。この記事では GW の間に自分で周辺ライブラリを使い倒しながら選定していった。

2021年 は Fullstack Next.js 元年なので、有望な Next.js 系フレームワークを全部試した の cloudflare-workers + d1 版とも言える。

tl;dr

  • cloudflare-pages-functions
  • remix | @remix-run/cloudflare-pages
  • remix-auth | Google OAuth
  • D1 | DrizzleORM
  • GitHub Actions CI and Release

選定ポリシー

  • 初期コスト0円(or cloudflare bundled プランで $5)からで、段階的にスケールする
  • 現時点で枯れてる必要はないので、ポテンシャル重視
    • d1 の open alpha が終わるまでに、下記の残タスクを潰していく。
  • パフォーマンス: 薄く、フレームワーク自体がオーバーヘッドにならないように。 プレーンなページでLighthouse 100点はマスト。
  • 開発体験
    • 宣言的マイグレーション
    • TypeScript との親和性
  • ビルドが軽量で高速
    • 開発環境のリロード速度に直結
    • cloudflare-workers の 1MB 制限に引っかからない
  • フルスタック≒Rails相当
    • ただし scaffold は不要。スタックが固まってない段階の scaffold は負債の高速生産。
  • 誰でも使えるというより、個人のパフォーマンスを最大化するスタック

念頭にあるもの

選定結果

  • プラットフォーム: cloudflare pages functions
    • 静的サイトホスティング + 部分的な worker の構成が柔軟に取れる
    • ただし各種環境の binding に難あり(後述)
  • フレームワーク: remix + @remix-run/cloudflare-pages
    • 他のフレームワークと開発ワークフローが大きく変わらない
  • DB: D1 + drizzle-orm
    • drizzle は癖があるが現状一択。prisma が D1 対応したら要検討
  • 認証: remix-auth + Google OAuth
    • Session 永続化は Workers KV
  • エラーメトリクス: sentry を使いたかったが...(後述)
  • テスト: vitest + vitest-environment-miniflare
    • ローカル環境で cloudflare stack をモックしてテストできる
  • CI: GitHub Actions

remix を選んだ理由について

SSR するかどうかは後で選べばいいとして、どっちでも使えるものを選んでおくのは生存戦略上有効。SSR するフレームワークをスクラッチで作るのは大変で、かつ選定後に各位がググりながら自走するためにある程度ノウハウが普及する程度のシェアが必要で、 実質 Next vs Remix になる。API エンドポイントを作るだけなら Hono でいいと思う。

https://github.com/honojs/hono

Next.js を cloudflare 上で動かす https://github.com/cloudflare/next-on-pages を試しに動かしてみたが、開発環境でちょっとした変更のたびに10秒以上のフルビルドを待つ必要があり、かなり厳しく感じた。 やはり Next は Vercel Stack で動かしたほうがよい。

Remix は最初から cloudflare-workers(-pages) に対応している。

Remix はSPA遷移してるうちは Next と同じような肌感なのだが、 組み込みの @remix-run/react の Form と組み合わせて使うと不思議な開発体験で、やってることは Form を作って POST を受けて結果を返して表示、という古のワークフローなのに、それが SPA上で動いている。

https://remix.run/docs/en/main/guides/data-writes

React の「サーバーサイドと同じく完成形のHTMLを常に宣言する」というのが広く刺さったように、 Remix の 「サーバーサイドと同じく form と CRUD エンドポイントで実装できる」というのが刺さるのではないか、という予感がある。

Remix は API エンドポイントと View の JSON の受け渡しが(Blitz.jsのように)フレームワーク内に隠蔽されてるので、自身に閉じてる限り tprc や graphql が不要なのもポイントが高い。

ORM

別記事に選定過程を書いたのでここで詳述しないが、奇しくも同じ時期に同じ内容を書いた @chimame さんと同じ結論に至っている。drizzle か kysely。

https://zenn.dev/mizchi/articles/d1-drizzle-orm

https://zenn.dev/chimame/articles/60e2824a42fec0

ただし、両者ともに prisma があったらそれを選びたい気持ちは一緒。今後の prisma 次第だが、prisma はビルドした際のサイズが結構大きいので、 cloudflare-workers 環境だとそれが問題になる可能性がある。

他に考えたこととして、drizzle, kyzery, prisma はいずれも ORM というより、クエリビルダといった趣が強いので、 ActiveRecord パターンで抽象するのは難しい気がしていて、 GraphQL や CQRS のように mutations | queries のようなディレクトリに分けたほうがよさそう。

app/
  routes/... # remix の routing
  mutations/
    createPost.ts
  queries/
    getPostWithTags.ts

これも最近のプラクティスとしては一般的な部類ではありそう。

認証周り

最初は IDaaS に Firebase Authentication の JWT Token を使おうと思ったが、 Remix のマインドセットに合わせるなら Session 認証したほうがいいだろうと思い、素朴な Google OAuth の認証を実装した。

現代の小規模サービスなんて email/password は脆弱なだけで、 OAuth さえあればいい。Twitter 認証が使い物にならなくなり、GitHub 認証は開発者しか通じないといった今、 最小限のテンプレートには Google OAuth だけあればよいと判断した。ちゃんとした IDaaS がほしければ、Auth0 を使えば良い。

ただし、Session の永続化に WorkersKV を使った。認証バックエンドはサービス全体のオーバーヘッドになるので可能な限り高速な必要があるが、WorkersKV なら簡単な RW が5~ms で終わるので。

残タスク

ここは触った過程で問題になった箇所を列挙している。

  • drizzle-orm + d1 で同名のカラムがある状態で join すると壊れる https://github.com/drizzle-team/drizzle-orm/issues/555
  • remix の server-side で Unhandle Error Rejection が出た時に server-side で捕捉する方法がない。これによって自分で補足する以外に sentry にデータを送る方法がない https://github.com/remix-run/remix/discussions/6313
  • clouflare pages functions が wrangler.toml の binding ([vars]kv_namespaces) を参照しない
    • cloudflare-developpers の discord で聞いたらバグとのこと。これは wrangler pages コマンド自体がβ版なので待てば治ると期待している。
  • d1 の open alpha 終了

個人タスク

感想

ここまで書いておいてなんだが、ボイラープレートを作ることは、他人に提供するものというより、自分自身のためのものだと思う。デッキビルディング型ローグライクで、自分の目指すビルドのためにカードをピックしていく感覚に近い。

自分が作ったボイラープレートを紹介して、「ここからいじっていくといい」と紹介するのもいいのだが、結局は自分で手を動かさないと腹落ちしないと感じた。特に今回のような寄せ集めスタックを活かす場合、自分と学んだ経路を整理して追体験して納得してもらう必要があり、それはたぶん一冊の本の形式を取ることになる。気が向いたら zenn の有料本で書くかもしれない。

いずれにせよ、そのような本で学習する場合、速習にはなるが自分でライブラリを漁る体験を飛ばすことになり、技術的な審美眼を鍛える機会が失われる。

Rails みたいなオールインワン環境はそれ自体が一種の権威みたいになっていて、そのチュートリアルを上からやっていくことに学習者は抵抗がないと思うが、寄せ集めスタックを紹介する場合はそうはいかないのが難しいと感じた。

あと一応、これを書いてる途中に vercel/kv とかも出たので、ちょっと vercel 側も調べたりしていたが、 Next は Vercel Stack or 静的サイトホスティングで、 Remix は Workers というのが安牌ではあると思う。

Discussion