🔥

Qwik City on Hono on Cloudflare Workers

2023/06/09に公開
1

Cloudflare Workers上のHonoでQwik Cityを動かそう!

この構成にした理由

Web標準のFetch APIベースでフロントエンドをやりたい

そろそろ行けるのでは…?標準のFetch APIでゴニョゴニョできる時代(?)

Cloudflare Workersで動かしたい

  • ドメインさえあれば無料!
  • CDNエッジで高速な配信!

Qwik Cityの開発者体験が滅茶苦茶いい

  • 関数を最大源活用することでReact(Next.js)の落とし穴的なところがかなり解消されている
    • useEffectが毎回実行される
    • これクライアントサイド?サーバサイド?どっちで実行?
  • 個人的にはNext.jsより好き
    • App Directoryの思想は好きだけどツールチェーンと安定性が…
  • 時代はSSR…
    • やっぱりデータフェッチが楽

Honoっていう早いフレームワークがあるらしい

  • 非常にシンプルな機能を持ったWebフレームワーク
    • Expressみたいな感じ
  • ミドルウェアで色々カスタマイズできる(今回はこれが重要)

QwikをHonoで動かせるの?

結論から言いますと割と簡単に動かせますし、ベースとなる部分はHonoのミドルウェアとして提供されています(Honoの作者によるリポジトリ)。これはHonoQwikFetch APIをベースに実装されており、HonoQwik City間のアダプタを実装することが比較的容易であるためです。実際に上記のミドルウェアには、HonoからQwik Cityのレンダリング関数を呼ぶためのミドルウェア(アダプタ)が定義されていますが、コードの本体は100行に満たないシンプルな実装になっています。なお、今回はQwik Cityを用いましたが、他にもRemixなどのFetch APIをベースに実装されたUIフレームワークであれば同様のことが比較的容易に実現できます(Honoの作者による記事、Next.jsにHonoを乗っけたりもできる)。

Honoの作者が対応済みならこの記事いらなくない?

と思われる方もいらっしゃると思いますが、意味はあります!具体的には、上記のミドルウェアで前提としているQwikのバージョンが古くて、最新のQwikが入ってるとpeer Dependencyで問題が発生します… またCloudflare Workers(wrangler)で動かそうとすると、デフォルトではホットリロードが効かないため、開発しにくいといった問題があります。そこで本記事ではホットリロードを実現しつつ、Qwik City on Honoで開発方法を紹介したいと思います。

環境構築

  • Node.js: 18.16.0
  • pnpm: 8.6.0
  • 配信環境: Cloudflare Workers
  • Qwik: 1.1.4
  • hono: 3.2.4
  • vite: 4.3.5

Qwikプロジェクトの作成

pnpm create qwik@latest
# こだわりがなければEnter連打
# 作成できたらディレクトリを移動しましょう

Honoの導入とQwik Cityミドルウェアの導入

Hono作者のリポジトリを参考に構築していきます。

pnpm add -D hono wrangler

touch vite.config.workers.ts
touch src/entry.hono.tsx
touch src/entry.workers.tsx

上記ライブラリの該当ソースをそれぞれのファイルにコピペしましょう。

mkdir -p src/lib/hono-middleware
touch src/lib/hono-middleware/qwik-city.ts

上記で生成したファイルにHonoのQwik Cityミドルウェアをコピペしましょう。その後、src/entry.hono.tsxのインポートを修正してください。

- import { qwikMiddleware } from '@hono/qwik-city'
+ import { qwikMiddleware } from "~/lib/hono-middleware/qwic-city";

本来であれば、既に提供されているパッケージをそのまま再利用したかったのですが、Qwikの依存関係が古かったこと、モノレポ構成でテストなどがよくわからない(そもそもない?)ため、このような形になりました。バージョンを上げても、元のコードそのままも動作することは確認できました。時間があればプルリクして貢献したいところです。

Hot Reload

まず初めに以下のファイルを生成しましょう。

touch wrangler.toml
touch wrangler.dev.toml
wrangler.toml
name = "{各自のアプリ名}" # デプロイ時のサブドメインになります
main = "server/entry.workers.mjs"
compatibility_date = "2023-01-10"

[site]
bucket = "./dist"

[build]
command = "pnpm run build"
wrangler.dev.toml
name = "{各自のアプリ名}"
main = "server/entry.workers.mjs"
compatibility_date = "2023-01-10"

[site]
bucket = "./dist"

[build]
command = "pnpm run build.client && pnpm run build.workers"

package.jsonを見ていただくとわかると思いますが、上記のビルドコマンドは設定されていません。こちらにもいくつか変更を加えます。

package.json
  "scripts": {
    "build": "pnpm run build.qwik && pnpm run build.workers",
    "build.qwik": "qwik build",
    "build.client": "vite build",
    "build.workers": "vite build -c vite.config.workers.ts",
    "build.types": "tsc --incremental --noEmit",
    "deploy": "wrangler publish server/entry.workers.mjs",
    "dev": "wrangler dev --live-reload -c ./wrangler.dev.toml server/entry.workers.mjs",
    "dev.debug": "node --inspect-brk ./node_modules/vite/bin/vite.js --mode ssr --force",
    "fmt.fix": "prettier --write .",
    "fmt.check": "prettier --check .",
    "lint": "eslint \"src/**/*.ts*\""
  },

上から順に解説します。

  • build.*
    • build.qwik
      デフォルトのbuildに相当するコマンドです。以下のコマンドを実行します。コンパイラとLinterによる検証をした上でクライアントサイド用のファイルを生成します。
      • pnpm run build.types
      • pnpm run build.client
      • pnpm run lint
    • build.workers
      Workers用のビルドコマンドです。
    • build
      上記の2コマンドを実行します。デプロイ用のコマンドであり、wrangler.tomlで実行します。
  • dev
    wranglerの開発サーバモードを実行します。wrangler.tomlをそのまま利用すると、ファイル更新時のビルドエラーで開発用サーバが終了してしまうため、dev用のファイルを用意しています。こちらでは、コンパイラやLinterによる検証なしで実行しています。また、--live-reloadオプションを付与することでホットリロードしています。
  • deploy
    wranglerでデプロイを行います。内部的には上記のbuildコマンドでビルドします(諸々の検証あり)。Cloudflareのアカウントを所持していればログインしたのち、30秒足らずでデプロイできると思います。このあたりの体験の良さは半端じゃないですね。
pnpm run dev # 開発サーバ起動
pnpm run deploy # 本番デプロイ

動いている様子

まとめ

大体こんな感じでホットリロードありの開発環境になります。ターミナルの挙動が若干うるさい(コマンドのログ無効化できる気もするけど)という難点はありますが、おおよそ不満なく動きます(Viteのビルドはやっぱり早い)。
Qwik City自体にmiddlewareに相当する機能やAPIサーバ機能がありますが、HonoにはCookieやCORS、Logger、認証機能が標準で備わっており、この辺りはやはりサーバサイドのフレームワークの強みですね… 認証システムをUIフレームワークから切り離すこともできそうなので、クライアントサイドで細かい動作をさせない限り、こちらの方がスッキリしそうです。Qwikのドキュメントを見るかぎり、Honoのミドルウェアで処理した内容をQwikに渡せるようですし。

最近ではNext.jsのApp RouterやServer Actionsが登場していますが、まだまだ不安定です。SSRやサーバとの統合という面ではQwik CityやHonoの方がまだまだ上のような気がします。QwikのStorybook対応が完璧でないという懸念点(記事を書いている時点ではStorybookは非同期コンポーネントに対応してなくて、App Directoryも微妙だけど…)もありますが、個人的には非常におすすめの技術です。今回は2つ合わせたアプリケーションを紹介しましたが、それぞれがCloudflareやVercelをはじめ、さまざまなプラットフォームで動作するフレームワークです。非常に汎用性に優れているため、一度触れてみると面白いと思います。

動く状態のリポジトリ

https://github.com/totto2727-org/qwik-with-hono

Discussion

yusukebeyusukebe

Honoの作者です。プルリクいただけると嬉しいです!