Open9

cloudflare Pagesにremixアプリをデプロイしていく

yuppeyuppe

デフォルトテンプレートのdependenciesを見るとNodejsランタイムが想定されているので、これをCloudflare Pages用に対応していく

 pnpm rm @remix-run/node
yuppeyuppe

cloudfrare用の開発環境周りセットアップ(インストール編)

  • cloudfrareのリソースいじるようのCLI追加
pnpm add -D wrangler
  • TS環境でCloudflare Pages Functions(Workers)を使うための型定義ファイルを導入
pnpm add @cloudflare/workers-types -D

今回はRemix自体に用意されたexpressベースのサーバーも使用しないので外す

pnpm rm @remix-run/serve
  • Cloudflare/PagesでRemixを起動するアダプターの追加
pnpm add @remix-run/cloudflare @remix-run/cloudflare-pages
yuppeyuppe

cloudfrare用の開発環境周りセットアップ(設定ファイル編)

  • typesをCloudflare用に変更
tsconfig.json
    "types": [
      "@remix-run/cloudflare",
      "vite/client",
      "@cloudflare/workers-types/2023-07-01"
    ],
  • 開発サーバー起動時にCloudflareランタイムをシュミレートするプラグイン追加
vite.config.ts
+ import { vitePlugin as remix, cloudflareDevProxyVitePlugin } from "@remix-run/dev";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";

export default defineConfig({
  plugins: [
+   cloudflareDevProxyVitePlugin(),
    remix({
      future: {
        v3_fetcherPersist: true,
        v3_relativeSplatPath: true,
        v3_throwAbortReason: true,
      },
    }),
    tsconfigPaths(),
  ],
});
yuppeyuppe

Cloudflare Pages用のfunctionを設定する

詳しくは

https://developers.cloudflare.com/pages/functions/get-started/

functions/[[path]].ts
import { createPagesFunctionHandler } from '@remix-run/cloudflare-pages'

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore - the server build file is generated by `remix vite:build`
// eslint-disable-next-line import/no-unresolved
import * as build from '../build/server'

export const onRequest = createPagesFunctionHandler({ build })

catch-allファイル名ルールを使用することで、全てのリクエストをRemix側に流し、ビルド後に生成されるbuild/serverファイルとPages Functionsの繋ぎ込みを行なっている

yuppeyuppe

Remixサーバー側エントリーポイントをCloudflare仕様にする

  • entry.server.tsxの編集
app/enter.server.tsx
/**
 * By default, Remix will handle generating the HTTP Response for you.
 * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨
 * For more information, see https://remix.run/file-conventions/entry.server
 */

import type { AppLoadContext, EntryContext } from "@remix-run/cloudflare"
import { RemixServer } from "@remix-run/react"
import { isbot } from "isbot"

// web標準のWeb Streamを生成する関数
import { renderToReadableStream } from "react-dom/server"

const ABORT_DELAY = 5_000;

export default async function handleRequest(
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  remixContext: EntryContext,
  // This is ignored so we can keep it in the template for visibility.  Feel
  // free to delete this parameter in your app if you're not using it!
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  loadContext: AppLoadContext
) {
  const body = await renderToReadableStream(
    <RemixServer context={remixContext} url={request.url} />,
    {
      signal: request.signal,
      onError(error: unknown) {
        // Log streaming rendering errors from inside the shell
        console.error(error)
        responseStatusCode = 500
      }
    }
  )

  if (isbot(request.headers.get("user-agent")) || "") {
    await body.allReady
  }

  responseHeaders.set("Content-Type", "text/html")
  return new Response(body, {
    headers: responseHeaders,
    status: responseStatusCode
  })
}
  • productionサーバー実行コマンドの書き換え
  "scripts": {
    "build": "remix vite:build",
    "dev": "remix vite:dev",
    "lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
-   "start": "remix-serve ./build/server/index.js",
+   "start": "wrangler pages dev build/client",
    "typecheck": "tsc"
  },
yuppeyuppe

本番設定同様のサーバーへのアクセス

npm run build && npm run start

> build
> remix vite:build

vite v5.3.2 building for production...
✓ 85 modules transformed.
build/client/.vite/manifest.json                1.12 kB │ gzip:  0.32 kB
build/client/assets/root-BFUH26ow.css           5.36 kB │ gzip:  1.61 kB
build/client/assets/_index-B6hwyHK-.js          0.94 kB │ gzip:  0.40 kB
build/client/assets/root-SDu10Ik2.js            1.44 kB │ gzip:  0.84 kB
build/client/assets/entry.client-DFN7WZku.js    4.11 kB │ gzip:  1.56 kB
build/client/assets/jsx-runtime-56DGgGmo.js     8.11 kB │ gzip:  3.05 kB
build/client/assets/components-DxtGWM4Z.js    239.14 kB │ gzip: 77.05 kB
✓ built in 1.18s
vite v5.3.2 building SSR bundle for production...
✓ 6 modules transformed.
build/server/.vite/manifest.json               0.22 kB
build/server/assets/server-build-BFUH26ow.css  5.36 kB
build/server/index.js                          5.15 kB
✓ built in 40ms

> start
> wrangler pages dev build/client


 ⛅️ wrangler 3.62.0
-------------------

✨ Compiled Worker successfully
✔ Would you like to help improve Wrangler by sending usage metrics to Cloudflare? … yes
Your choice has been saved in the following file: ../../../../../Library/Preferences/.wrangler/metrics.json.

  You can override the user level setting for a project in `wrangler.toml`:

   - to disable sending metrics for a project: `send_metrics = false`
   - to enable sending metrics for a project: `send_metrics = true`
[wrangler:inf] Ready on http://localhost:8788

✌️

yuppeyuppe

実際にCloudflare Pagesへデプロイしていく

やること

  • Cloudflareアカウント作成
  • gitignoreに.wranglerフォルダ追加
  • GitHubへpush
  • Cloudflare Pagesで公開設定