Remix on Cloudflare Workers そして Remix on Hono
これなに
- Remix on Cloudflare Workers始めたいな~とチュートリアルを探すと見つけられず、とりあえずのチュートリアルを自分向けに作りたかった。
- せっかくRemixを動かすならどこかでみた、Honoのmount機能を試してみたかった。
1. Remix on Cloudflare Workers
create-remix
でプロジェクトを作る。
npx create-remix@latest
Need to install the following packages:
create-remix@1.19.3
Ok to proceed? (y) y
? Where would you like to create your app? ./my-remix-app
? What type of app do you want to create? Just the basics
? Where do you want to deploy? Choose Remix App Server if you're unsure; it's easy to change deployment targets.
Cloudflare Workers
? TypeScript or JavaScript? TypeScript
? Do you want me to run `npm install`? Yes
- Gitpodで作っていて、最初Cloudflareの認証周りでハマった。 Cloudflareは時限付きのAPIキーを払い出せるようなので、設定して解決した。
# install CLI
npm install -g wrangler@latest
# API KEY
export CLOUDFLARE_API_TOKEN=<API KEY>
# Deploy
wrangler deploy --name <name>
これだけでシンプルなページが公開できる。
2. Remix on Hono
NG Pattern
の記事にコードがあるので、my-remix-app/server.ts
を書き換えるだけかと思ったら、ビルド時に Could not resolve "__STATIC_CONTENT_MANIFEST"
エラーが出てしまった。
エラーメッセージで調べるとHonoのIssueが出てくるも、バグとかではなさそう。少なくともこれでは不十分みたいだ。
Setting
よくよく調べるとyusukebe氏が動くパターンを公開しているので、これに倣ってcreate-remixで作った設定を変更していく。
package.json
エントリーポイントを指定する必要がありそう。ついでにminifyも入れる。
{
"private": true,
"sideEffects": false,
"type": "module",
"scripts": {
"build": "remix build",
- "deploy": "wrangler deploy",
+ "deploy": "wrangler publish --minify server.ts",
"dev": "remix dev --manual -c \"npm start\"",
"start": "wrangler dev ./build/index.js",
"typecheck": "tsc"
},
"dependencies": {
"@cloudflare/kv-asset-handler": "^0.1.3",
"@remix-run/cloudflare": "^1.19.3",
"@remix-run/css-bundle": "^1.19.3",
"@remix-run/react": "^1.19.3",
+ "hono": "^3.5.6",
"isbot": "^3.6.8",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@cloudflare/workers-types": "^3.19.0",
"@remix-run/dev": "^1.19.3",
"@remix-run/eslint-config": "^1.19.3",
"@types/react": "^18.0.35",
"@types/react-dom": "^18.0.11",
"eslint": "^8.38.0",
"typescript": "^5.0.4",
"wrangler": "^3.1.1"
},
"engines": {
"node": ">=16.13.0"
}
}
remix.config.js
Service WorkerからModule Workerに変更しているという情報を見た気がするが、実のところこの辺は雰囲気でやっている(恥)
/** @type {import('@remix-run/dev').AppConfig} */
export default {
ignoredRouteFiles: ["**/.*"],
- server: "./server.ts",
- serverConditions: ["workerd", "worker", "browser"],
+ serverConditions: ['worker'],
- serverDependenciesToBundle: [
- // bundle verything except the virtual module for the static content manifest provided by wrangler
- /^(?!.*\b__STATIC_CONTENT_MANIFEST\b).*$/,
- ],
+ serverDependenciesToBundle: 'all',
serverMainFields: ["browser", "module", "main"],
serverMinify: true,
serverModuleFormat: "esm",
serverPlatform: "neutral",
// appDirectory: "app",
// assetsBuildDirectory: "public/build",
// serverBuildPath: "build/index.js",
// publicPath: "/build/",
future: {
v2_dev: true,
v2_errorBoundary: true,
v2_headers: true,
v2_meta: true,
v2_normalizeFormMethod: true,
v2_routeConvention: true,
},
};
remix.env.d.ts
Workers KVへ格納される静的ファイルの型情報が__STATIC_CONTENT_MANIFEST
らしい。今回は使わないので無効化してるのかな。
/// <reference types="@remix-run/dev" />
/// <reference types="@remix-run/cloudflare" />
/// <reference types="@cloudflare/workers-types" />
-
- declare module "__STATIC_CONTENT_MANIFEST" {
- const manifest: string;
- export default manifest;
- }
Code
ただラップするだけでは芸がないので、以下のMiddlewareを入れつつ、シンプルに動かしてみる。
yusukebe/remix-on-hono
方式
1. c.req.raw
をhandlerに渡して、無事に動いた!
import { Hono } from 'hono'
import { basicAuth } from 'hono/basic-auth'
import { secureHeaders } from 'hono/secure-headers'
import { createRequestHandler } from '@remix-run/cloudflare'
import * as build from './build'
// @ts-ignore
const handleRemixRequest = createRequestHandler(build, process.env.NODE_ENV)
const app = new Hono()
app.use(
'*',
basicAuth({
username: 'hono',
password: 'acoolproject',
})
)
app.use('*', secureHeaders())
app.get('*', async (c) => {
return await handleRemixRequest(c.req.raw)
})
export default app
2. app.mount
app.mount
のドキュメントにはRemixの例がないが、色々見たら無事に動いた!
import { Hono } from 'hono'
import { basicAuth } from 'hono/basic-auth'
import { secureHeaders } from 'hono/secure-headers'
import { createRequestHandler } from '@remix-run/cloudflare'
import * as build from './build'
// @ts-ignore
const remixHandler = createRequestHandler(build, process.env.NODE_ENV)
const app = new Hono(
app.use(
'*',
basicAuth({
username: 'hono',
password: 'acoolproject',
})
)
app.use('*', secureHeaders())
app.mount('/', remixHandler)
export default app
3. mountできないケース?
app.mount('/remix', remixHandler)の様にしてCloudflare workersにデプロイすると、アクセス先でDDosっぽい動きになって困った。私の修正が間違っていそうな気がするが後日Issueを挙げておく。
おわりに
ということで基本的なBootstrapはできたので、必要に応じてRemix+Cloudflare Workersを使うのも怖くない! Honoでミドルウェアや値を渡すのもサクサクできそう。
調べて出てきたのだが、サードパーティのMiddlewareも見つけたので、コードを読んで良さそうならこれを選ぶのもあり。
直接関係ないけど、とにかくCloudflare Workersのデプロイにかかる時間が短いので、トライ&エラーが苦にならないのが良いな~と感じました。
Discussion