Cloudflare Pagesの開発でVite使う
Cloudflare Pagesの開発にはwrangler pages devが使えるんだけど、引数にコマンドライン渡せるの知らなかった。引数に渡したコマンドが開いたポートをリバースプロキシしてくれる。つまり、viteコマンドを渡せばViteのdevサーバーをバックエンドにしたWranglerが立ち上がり、裏側ではHMRが効く。これ何が嬉しいって、/functionsでAPIを作った場合にこれまでWranglerとVite2つ立ち上げていたのが1つで済むし、クライアントもAPIサーバーもホットリロード効くのでDXが良い。そして、これを利用すると例えば非常に少ないファイルでReactのSPAが作れる。
まずWranglerにコマンドライン渡せるやつはこんな感じ。
wrangler pages dev --local -- vite
以下のスクショだと、Viteのdevサーバーが5175で立ったのを検出してWranglerがプロキシしている。

ではこれを利用して、簡単なAPIとそれを受け取って描画するReactアプリを作ってみる。
Pagesでは/functions以下に置いたTypeScriptをAPIのようにすることができる。Next.jsのpages/apiみたいなの。onRequestをexportする。
export function onRequest(context) {
return new Response("Hello, world!")
}
素で書いてもいいが、Webフレームワークを使う。Honoの次期バージョンではCloudflare Pagesのアダプタが用意される。
import { Hono } from 'hono'
import { handle } from 'hono/cloudflare-pages'
const app = new Hono()
app.get('/api', (c) => {
return c.json({
message: 'Hello',
})
})
export const onRequest = handle(app)
これを /functions/api/[[route]].tsに置けば、/api以下をハンドリングして、Honoのアプリで扱うことができる。c.reqを使えばリクエストの値を取れるし、c.header()でレスポンスヘッダを追加したりできる。ふつうにWorkersのアプリを書けるわけだ。
Bindingsも取れた。Pagesは.dev.varsに環境変数を書く。
NAME=Hono
こうすると、いつものHonoアプリと同じようにc.env.NAMEでとれる。Honoに型を渡せば補完も効く。

Next.jsがPagesで動くけど、たしか環境変数とれないので、その点優れている。なにより、KVやD1も使える(はず)!
さてこれで/apiにAPIが生えたので、それをReactで受け取る。Viteはindex.htmlをエントリポイントにするので、空のHTMLを置く。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
src/main.tsxはコンポーネントを受け取って、レンダリングしているだけ。Appのように切り出さないとViteのFast Refreshが効かない。
import ReactDOM from 'react-dom/client'
import App from './App'
ReactDOM.createRoot(document.getElementById('root')).render(<App />)
src/App.tsxではふつうに/apiを叩ける。
import { useState, useEffect } from 'react'
const App = () => {
const [message, setMessage] = useState('')
useEffect(() => {
const fetchData = async () => {
const res = await fetch('/api')
const data = await res.json()
setMessage(data.message)
}
fetchData()
}, [])
return <h1>{message}</h1>
}
export default App
この開発環境だと、クライントはもちろんだし、サーバー側を変更してもリロードが効く。

フレームワークを使わずにWranglerとViteでできちゃうのがよい。
それで、HonoにはtRPC Serverミドルウェアもあるので、tRPCもふつーにうごく。最小限の構成で動く。
Honoのアプリ。
import { Hono } from 'hono'
import { handle } from 'hono/cloudflare-pages'
import { trpcServer } from '@hono/trpc-server'
import { appRouter } from '../../src/router'
const app = new Hono()
app.all(
'*',
trpcServer({
router: appRouter,
})
)
export const onRequest = handle(app)
クライントでは補完が効く。


これはよい。
ちなみにHonoのv3ではRPC機能が追加され、tRPCよりもっとカジュアルにRPC機能が使える予定。

脱線しましたが、Pages + Viteの開発環境はよさそうという話でした。
コードは以下に置いてあります。
Discussion
undocumentedですが
wrangler pages devでプロキシするコマンドは実はnode_modules/.binにパスが通ったような状態になっていてnpxやyarnなしで動きます.なので,wrangler pages dev -- viteで十分でscripts欄が大差ないですが僅かにすっきりします.まさに今回の記事の構成でHonoとPages Functionsを使って開発しているのですが,動いています!まだデプロイしたことなくてローカルのみでの確認ですが...
P.S.
hcが入ったRCのリリース楽しみに待ってます!コマンドは、そうですね!修正しておきました。ありがとうございます。
hc入ったRCはもうすぐ出します!そして、意見を募ろうと思ってます。試しました!...が環境変数NAMEがとれず、undefinedに。
ファイル名dev.envではなくdev.varsだと環境変数がとれましたのでご報告します!
うわー。時間を無駄にさせてしまいました。すいません><
記事を修正しておきました!ありがとうございます!