本文へ移動

Cloudflare Pages ​

注意

新しいプロジェクトでは、Cloudflare は現在、Cloudflare Pages よりも Cloudflare Workers を推奨しています。Workers は静的アセットに対応し、より幅広い機能を提供します。新しいフルスタックアプリケーションを始める場合は、この Pages 構成の後継となる Cloudflare Workers + Vite を参照してください。hono/cloudflare-pages アダプターは非推奨で、Hono v5 で削除される予定です。

Cloudflare Pages はフルスタック Web アプリケーション向けのエッジプラットフォームです。 静的ファイルと、Cloudflare Workers が生成する動的コンテンツを配信します。

Hono は Cloudflare Pages を完全にサポートしています。 快適な開発体験を提供します。Vite の開発サーバーは高速で、Wrangler を使ったデプロイも非常にすばやく行えます。

1. セットアップ ​

Cloudflare Pages 向けのスターターテンプレートがあります。 「create-hono」コマンドでプロジェクトを開始します。 この例では cloudflare-pages テンプレートを選択します。

sh
npm create hono@latest my-app
sh
yarn create hono my-app
sh
pnpm create hono my-app
sh
bun create hono@latest my-app
sh
deno init --npm hono my-app

my-app に移動して依存関係をインストールします。

sh
cd my-app
npm i
sh
cd my-app
yarn
sh
cd my-app
pnpm i
sh
cd my-app
bun i

基本的なディレクトリ構成は次のとおりです。

text
./
├── package.json
├── public
│   └── static // Put your static files.
│       └── style.css // You can refer to it as `/static/style.css`.
├── src
│   ├── index.tsx // The entry point for server-side.
│   └── renderer.tsx
├── tsconfig.json
└── vite.config.ts

2. Hello World ​

次のように src/index.tsx を編集します。

tsx
import { Hono } from 'hono'
import { renderer } from './renderer'

const app = new Hono()

app.get('*', renderer)

app.get('/', (c) => {
  return c.render(<h1>Hello, Cloudflare Pages!</h1>)
})

export default app

3. 実行 ​

開発サーバーをローカルで起動し、Web ブラウザーで http://localhost:5173 にアクセスします。

sh
npm run dev
sh
yarn dev
sh
pnpm dev
sh
bun run dev

4. デプロイ ​

Cloudflare アカウントがあれば、Cloudflare にデプロイできます。package.json の $npm_execpath は、使用するパッケージマネージャーに変更する必要があります。

sh
npm run deploy
sh
yarn deploy
sh
pnpm run deploy
sh
bun run deploy

GitHub を使って Cloudflare ダッシュボードからデプロイする ​

  1. Cloudflare ダッシュボードにログインし、アカウントを選択します。
  2. アカウントのホームで Workers & Pages > Create application > Pages > Connect to Git を選択します。
  3. GitHub アカウントを認証し、リポジトリを選択します。Set up builds and deployments で、次の情報を入力します。
設定項目値
本番ブランチmain
ビルドコマンドnpm run build
ビルドディレクトリdist

バインディング ​

変数、KV、D1 などの Cloudflare バインディングを利用できます。 このセクションでは、変数と KV を使います。

wrangler.toml の作成 ​

まず、ローカルのバインディング用に wrangler.toml を作成します。

sh
touch wrangler.toml

wrangler.toml を編集し、MY_NAME という名前の変数を指定します。

toml
[vars]
MY_NAME = "Hono"

KV の作成 ​

次に KV を作成します。以下の wrangler コマンドを実行してください。

sh
wrangler kv namespace create MY_KV --preview

次の出力にある preview_id を控えておきます。

{ binding = "MY_KV", preview_id = "abcdef" }

preview_id とバインディング名 MY_KV を指定します。

toml
[[kv_namespaces]]
binding = "MY_KV"
id = "abcdef"

vite.config.ts の編集 ​

vite.config.ts を編集します。

ts
import devServer from '@hono/vite-dev-server'
import adapter from '@hono/vite-dev-server/cloudflare'
import build from '@hono/vite-cloudflare-pages'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    devServer({
      entry: 'src/index.tsx',
      adapter, // Cloudflare Adapter
    }),
    build(),
  ],
})

アプリケーションでバインディングを使う ​

アプリケーションで変数と KV を使い、型を設定します。

ts
type Bindings = {
  MY_NAME: string
  MY_KV: KVNamespace
}

const app = new Hono<{ Bindings: Bindings }>()

次のように利用します。

tsx
app.get('/', async (c) => {
  await c.env.MY_KV.put('name', c.env.MY_NAME)
  const name = await c.env.MY_KV.get('name')
  return c.render(<h1>Hello! {name}</h1>)
})

本番環境 ​

Cloudflare Pages では、ローカル開発に wrangler.toml を使い、本番環境のバインディングはダッシュボードで設定します。

クライアント側 ​

Vite の機能を使ってクライアント側のスクリプトを作成し、アプリケーションにインポートできます。 クライアントのエントリーポイントが /src/client.ts なら、script タグに指定するだけです。 また、import.meta.env.PROD を使うと、開発サーバーで実行されているか、ビルド段階かを判別できます。

tsx
app.get('/', (c) => {
  return c.html(
    <html>
      <head>
        {import.meta.env.PROD ? (
          <script type='module' src='/static/client.js'></script>
        ) : (
          <script type='module' src='/src/client.ts'></script>
        )}
      </head>
      <body>
        <h1>Hello</h1>
      </body>
    </html>
  )
})

スクリプトを正しくビルドするには、次の vite.config.ts の設定例を利用できます。

ts
import pages from '@hono/vite-cloudflare-pages'
import devServer from '@hono/vite-dev-server'
import { defineConfig } from 'vite'

export default defineConfig(({ mode }) => {
  if (mode === 'client') {
    return {
      build: {
        rollupOptions: {
          input: './src/client.ts',
          output: {
            entryFileNames: 'static/client.js',
          },
        },
      },
    }
  } else {
    return {
      plugins: [
        pages(),
        devServer({
          entry: 'src/index.tsx',
        }),
      ],
    }
  }
})

次のコマンドでサーバーとクライアントのスクリプトをビルドできます。

sh
vite build --mode client && vite build

Cloudflare Pages のミドルウェア ​

Cloudflare Pages は、Hono とは異なる独自のミドルウェアシステムを使います。次のように _middleware.ts というファイルから onRequest をエクスポートすると有効になります。

ts
// functions/_middleware.ts
export async function onRequest(pagesContext) {
  console.log(`You are accessing ${pagesContext.request.url}`)
  return await pagesContext.next()
}

handleMiddleware を使うと、Hono のミドルウェアを Cloudflare Pages のミドルウェアとして利用できます。

ts
// functions/_middleware.ts
import { handleMiddleware } from 'hono/cloudflare-pages'

export const onRequest = handleMiddleware(async (c, next) => {
  console.log(`You are accessing ${c.req.url}`)
  await next()
})

Hono の組み込みミドルウェアやサードパーティのミドルウェアも使えます。たとえば Basic 認証を追加するには、Hono の Basic 認証ミドルウェアを利用できます。

ts
// functions/_middleware.ts
import { handleMiddleware } from 'hono/cloudflare-pages'
import { basicAuth } from 'hono/basic-auth'

export const onRequest = handleMiddleware(
  basicAuth({
    username: 'hono',
    password: 'acoolproject',
  })
)

複数のミドルウェアを適用したい場合は、次のように記述します。

ts
import { handleMiddleware } from 'hono/cloudflare-pages'

// ...

export const onRequest = [
  handleMiddleware(middleware1),
  handleMiddleware(middleware2),
  handleMiddleware(middleware3),
]

EventContext へのアクセス ​

handleMiddleware の中では、c.env を通じて EventContext オブジェクトにアクセスできます。

ts
// functions/_middleware.ts
import { handleMiddleware } from 'hono/cloudflare-pages'

export const onRequest = [
  handleMiddleware(async (c, next) => {
    c.env.eventContext.data.user = 'Joe'
    await next()
  }),
]

その後、ハンドラー内で c.env.eventContext を通じてデータにアクセスできます。

ts
// functions/api/[[route]].ts
import type { EventContext } from 'hono/cloudflare-pages'
import { handle } from 'hono/cloudflare-pages'

// ...

type Env = {
  Bindings: {
    eventContext: EventContext
  }
}

const app = new Hono<Env>().basePath('/api')

app.get('/hello', (c) => {
  return c.json({
    message: `Hello, ${c.env.eventContext.data.user}!`, // 'Joe'
  })
})

export const onRequest = handle(app)

MIT ライセンスで公開されています。