remixとhonoを使う
環境作成
$ pnpm create cloudflare@latest
フレームワークに remix を使うため、以下の質問に対して回答する。
╭ Create an application with Cloudflare Step 1 of 3
│
├ In which directory do you want to create your application?
│ dir ./my-app
│
├ What would you like to start with?
│ category Framework Starter
│
├ Which development framework do you want to use?
│ framework Remix
versions
ライブラリ | バージョン |
---|---|
pnpm | 9.1.4 |
react | 18.2.0 |
remix-run/react | 2.10.3 |
@remix-run/cloudflare | 2.10.3 |
hono | 4.5.5 |
vite | 5.1.0 |
$ cd my-app
こちらのリポジトリを参考に、honoを導入する。
$ pnpm add hono
$ pnpm add -D @hono/vite-dev-server
ルートにserver.ts を作成する
import type { AppLoadContext } from "@remix-run/cloudflare";
import { createRequestHandler } from "@remix-run/cloudflare";
import { Hono } from "hono";
const app = new Hono();
app.get("/api/hello", (c) => {
return c.json({ message: "🔥" });
});
app.all("*", async (c) => {
try {
// @ts-expect-error it's not typed
// eslint-disable-next-line import/no-unresolved
const build = await import("virtual:remix/server-build");
const handler = createRequestHandler(build, "development");
const remixContext = {
cloudflare: {
env: c.env,
},
} as unknown as AppLoadContext;
return handler(c.req.raw, remixContext);
} catch (error) {
console.error(error);
process.exit(1);
}
});
export default app;
ついでに /api/hello
にAPIエンドポイントを作成しておく。
vite.config.ts を編集する
import devServer, { defaultOptions } from "@hono/vite-dev-server";
import adapter from "@hono/vite-dev-server/cloudflare";
import { vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";
export default defineConfig({
ssr: {
resolve: {
externalConditions: ["workerd", "worker"],
},
},
plugins: [
remix(),
devServer({
adapter,
entry: "server.ts",
exclude: [...defaultOptions.exclude, "/assets/**", "/app/**"],
injectClientScript: false,
}),
],
});
functions/[[path]].ts を編集する
import { handle } from 'hono/cloudflare-pages'
import server from '../server'
export const onRequest = handle(server)
開発サーバーを立て、http://localhost:5173 にブラウザでアクセスすると remix で作られたコンポーネントを表示する。
また、 http://localhost:5173/api/hello に curl コマンドでアクセスすると、server.ts で定義した通り{ message: "🔥" }
という Json の値が返却される。
loader内でfetchする
loader 関数内で fetch して コンポーネントから useLoaderData 経由で値を取得する。
export const loader = async () => {
const response = await fetch("http://localhost:5173/api/hello");
const data = (await response.json()) as { message: string };
return json({ data });
};
export default function Index() {
const { data } = useLoaderData<typeof loader>();
return (
<div className="font-sans p-4">
<h1 className="text-3xl">Welcome to Remix on Cloudflare</h1>
{data && <p>{data.message}</p>}
</div>
);
}
http://localhost:5173 にアクセスすると、api/hello から {message: "🔥"} を取得して表示していることが確認できる。
develop環境とproduction環境でfetchするURLが異なるので、環境変数で管理する。
cloudflareではwrangler.tomlで環境変数を設定できる。
name = "my-app"
pages_build_output_dir = "build/client"
[vars]
API_HOST = "http://localhost:5173"
[env.production]
[env.production.vars]
API_HOST = "https://XXX.my-app-XXX.pages.dev"
wrangler.tomlを参照して環境変数の型を生成する
$ pnpm wrangler types
interface Env {
+ API_HOST: "http://localhost:5173";
}
loader関数内で環境変数を読み込みfetchする
export const loader = async ({ context }: LoaderFunctionArgs) => {
const host = context.cloudflare.env.API_HOST;
const response = await fetch(`${host}/api/hello`);
const data = (await response.json()) as { message: string };
return json({ data });
};
デプロイする
コマンド一つでデプロイできる。
$ pnpm run deploy
pnpmを使う場合、もともと存在するpnpm deploy
とかぶってしまうため、package.jsonのscriptsに記載したdeploy
を実行する場合には pnpm run deploy
のようにrunをつける必要がある。