📃

Cloudflare Pagesの開発でVite使う

2023/02/07に公開
4

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がプロキシしている。

SS

ではこれを利用して、簡単なAPIとそれを受け取って描画するReactアプリを作ってみる。

Pagesでは/functions以下に置いたTypeScriptをAPIのようにすることができる。Next.jsのpages/apiみたいなの。onRequestexportする。

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に型を渡せば補完も効く。

SS

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

この開発環境だと、クライントはもちろんだし、サーバー側を変更してもリロードが効く。

SC

フレームワークを使わずに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)

クライントでは補完が効く。

SS

SS

これはよい。

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

SS

脱線しましたが、Pages + Viteの開発環境はよさそうという話でした。

コードは以下に置いてあります。

https://github.com/yusukebe/hono-examples

Discussion

Yudai NakataYudai Nakata

undocumentedですがwrangler pages devでプロキシするコマンドは実はnode_modules/.binにパスが通ったような状態になっていてnpxyarnなしで動きます.なので,wrangler pages dev -- viteで十分でscripts欄が大差ないですが僅かにすっきりします.

なにより、KVやD1も使える(はず)!

まさに今回の記事の構成でHonoとPages Functionsを使って開発しているのですが,動いています!まだデプロイしたことなくてローカルのみでの確認ですが...

P.S. hcが入ったRCのリリース楽しみに待ってます!

yusukebeyusukebe

コマンドは、そうですね!修正しておきました。ありがとうございます。

hc入ったRCはもうすぐ出します!そして、意見を募ろうと思ってます。

umeyukiumeyuki

試しました!...が環境変数NAMEがとれず、undefinedに。

Pagesは.dev.envに環境変数を書く。

ファイル名dev.envではなくdev.varsだと環境変数がとれましたのでご報告します!

yusukebeyusukebe

うわー。時間を無駄にさせてしまいました。すいません><
記事を修正しておきました!ありがとうございます!