📚

Remix on Hono on Cloudflare Workers

2024/11/11に公開

9/26 Cloudflare Workersが静的アセットの配信に対応するようになりました。
https://blog.cloudflare.com/builder-day-2024-announcements/#static-asset-hosting

これまで

Remix on Honoしたい
-> Workersにデプロイすると、Workersが静的アセットを返すのがイマイチ
-> イマイチだからPagesにデプロイするけど、honoと組み合わせるのが少しめんどくさい

みたいになっていた悩みが解決しそうです。

Static Assetsのドキュメント

https://developers.cloudflare.com/workers/static-assets/

  • リクエストが静的アセットに一致すれば静的アセットを返し、一致しなければworkerを実行する
  • 静的アセットへのリクエストは無料で無制限
  • 静的アセットの設定
    wrangler.toml
    assets = { directory = "./public/" }
    
    # こうすればworker内からも参照できる(env.ASSETS)
    assets = { directory = "./public/", binding = "ASSETS" }
    

動作確認

リポジトリ
https://github.com/shnakatani/remix-on-cloudflare-workers

セットアップ

こちらのページを参考にセットアップします。
https://developers.cloudflare.com/workers/frameworks/framework-guides/remix/

pnpm create cloudflare@latest remix-on-cloudflare-workers --framework=remix --experimental

デプロイしてみる

cd remix-on-cloudflare-workers
pnpm run deploy

Cloudflareのコンソールを確認しに行くとデプロイメントが2つ増えています。
1つ目のバージョンIDを確認すると

Assets have not yet been deployed...

とのこと。2つ目のバージョンID(latest)を確認すると

表示されました。

Honoを追加する(11/13更新)

記事作成と同日にhono-remix-adapterがworkers対応していたので、そちらを利用するように更新しました。
https://github.com/yusukebe/hono-remix-adapter

pnpm add hono hono-remix-adapter
pnpm add -D @hono/vite-dev-server
  1. Honoのエントリとしてserver/index.tsを作成
server/index.ts
import { Hono } from "hono";
import { poweredBy } from "hono/powered-by";

const app = new Hono();
app.use(poweredBy());
app.get("/api", (c) => {
  return c.json({ message: "Hello, World!" });
});

export default app;
vite.config.ts
-    cloudflareDevProxyVitePlugin({
-      getLoadContext,
-    }),
+    cloudflareDevProxyVitePlugin(),
    // ...
+    serverAdapter({
+      adapter,
+      getLoadContext,
+      entry: "server/index.ts",
+    }),
  1. Cloudflare Workersのエントリとしてworker.tsを作成
worker.ts
import handle from "hono-remix-adapter/cloudflare-workers";
// @ts-ignore This file won’t exist if it hasn’t yet been built
import * as build from "./build/server";
import { getLoadContext } from "./load-context";
import server from "./server";

export default handle(build, server, { getLoadContext });
wrangler.toml
-main = "./server.ts"
+main = "./worker.ts"
11/13更新前
pnpm add hono

server.tsを書き換えます。

server.ts
export default {
  async fetch(request, env, ctx) {
    try {
      const loadContext = getLoadContext({
        request,
        context: {
          cloudflare: {
            cf: request.cf,
            ctx: {
              waitUntil: ctx.waitUntil.bind(ctx),
              passThroughOnException: ctx.passThroughOnException.bind(ctx),
            },
            caches,
            env,
          },
        },
      });
      return await handleRemixRequest(request, loadContext);
    } catch (error) {
      console.log(error);
      return new Response("An unexpected error occurred", { status: 500 });
    }
  },
} satisfies ExportedHandler<Env>;


こんな感じ

server.ts
>const app = new Hono();

>app.use(poweredBy());

>app.use("*", async (c) => {
>  const request = c.req.raw;
>  const ctx = c.executionCtx;

   try {
     const loadContext = getLoadContext({
       request,
       context: {
         cloudflare: {
           cf: request.cf,
           ctx: {
             waitUntil: ctx.waitUntil.bind(ctx),
             passThroughOnException: ctx.passThroughOnException.bind(ctx),
           },
           caches,
>          env: env(c),
         },
       },
     });
     return await handleRemixRequest(request, loadContext);
   } catch (error) {
     console.log(error);
     return new Response("An unexpected error occurred", { status: 500 });
   }
 });

>export default app;

デプロイしてみる

pnpm run deploy

ページにアクセスするとx-powered-by: Honoとなっていることが確認できました。

参考

https://zenn.dev/chimame/articles/954fc60df7463a#静的ファイルの配信でworkersは動作しない(動作カウントに入らない)

Discussion