Azure FunctionsでHonoを動かすテンプレは公式に書いてますがWeb Appsだと無いみたいでした。
あとWeb AppsじゃなくてStatic Web Appsにデプロイするネタはたまに見かけますが、PaaSサービスのWeb Appsにデプロイしたいなと思った次第です。
Web Appsはコードデプロイで立てておく
コンテナ起動かコードデプロイかのどちらかがありますが、コードデプロイでNode.js 20LTSを選択します。
v22などは現状選択不可でした。
- リージョン: West US
- プラン: B1
修正箇所コピペ用
まずはNode.jsのテンプレートでアプリ作成
$ npm create hono@latest my-app
Which template do you want to use?で nodejs
を選択します。
ファイルたちが生成されます。
packege.json
とsrc/index.ts
を以下のように書き換えます。
import { serve } from '@hono/node-server'
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => {
return c.text('Hello Hono!')
})
const port = process.env.PORT ? parseInt(process.env.PORT, 10) : 3000;
console.log(`Server is running on http://localhost:${port}`)
serve({
fetch: app.fetch,
port
})
{
"name": "hono-node",
"type": "module",
"scripts": {
"start": "node --experimental-specifier-resolution=node --import tsx src/index.ts",
"dev": "tsx watch src/index.ts"
},
"dependencies": {
"@hono/node-server": "^1.13.4",
"hono": "^4.6.8"
},
"devDependencies": {
"@types/node": "^20.11.17",
"tsx": "^4.7.1"
}
}
あとはデプロイすればOKです。
GitHub経由でのデプロイがオススメですがデフォルトで作成されるActionsファイルが古いので差し替えると良いです。
ポイント解説など
解説というかハマったところです。
ポート
Azure Web Appsだとprocess.env.PORT
にサーバーの起動ポートが入っていく形になるので
Honoデフォルトのテンプレで作成されるコードにある3000番ポート指定から変えてあげます。
省略
const port = 3000
省略
↓変更後
省略
const port = process.env.PORT ? parseInt(process.env.PORT, 10) : 3000;
省略
起動方法
Azure Web Appsだと起動時にnpm run start
(npm start
)をするっぽいのでscriptsにstartの記述が欲しいです。
また、 "start": "tsx src/index.ts"
で試したところ、Azure Web Appsで何億回もみる:( Application Error
となりました。
ログストリームでログを見たところ、
2024-11-04T11:12:23.786301046Z > start
2024-11-04T11:12:23.786307346Z > tsx src/index.ts
2024-11-04T11:12:23.786311447Z
2024-11-04T11:12:24.729976104Z (node:100) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
2024-11-04T11:12:24.730062905Z (Use `node --trace-warnings ...` to show where the warning was created)
2024-11-04T11:12:24.815200758Z /home/site/wwwroot/node_modules/.bin/tsx:2
.
.
.
.
e}a(De,"g");var xu=a(t=>Math.max(...t.split(`
2024-11-04T11:12:24.816810474Z ^^^^^^
2024-11-04T11:12:24.816814874Z
2024-11-04T11:12:24.816818274Z SyntaxError: Cannot use import statement outside a module
2024-11-04T11:12:24.816821674Z at wrapSafe (node:internal/modules/cjs/loader:1281:20)
2024-11-04T11:12:24.816825074Z at Module._compile (node:internal/modules/cjs/loader:1321:27)
2024-11-04T11:12:24.816828574Z at Module._extensions..js (node:internal/modules/cjs/loader:1416:10)
2024-11-04T11:12:24.816831974Z at Module.load (node:internal/modules/cjs/loader:1208:32)
2024-11-04T11:12:24.816835275Z at Module._load (node:internal/modules/cjs/loader:1024:12)
2024-11-04T11:12:24.816838775Z at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:174:12)
2024-11-04T11:12:24.816842275Z at node:internal/main/run_main_module:28:49
といったエラーになっていました。
GPT-4oに聞いたところ
原因: tsx コマンドを使用していますが、Node.js環境がモジュール形式を適切に解釈していない可能性があります。
• 解決策:
• tsx は通常ESモジュールと互換性があるはずですが、Azureの環境で何らかの理由で問題が発生しているようです。
• "type": "module" が設定されている package.json を確認してありますが、問題が解決しない場合は以下を試してください:
1. Node.js のオプションを明示的に指定:
• スクリプト内でtsxを呼び出す際に、nodeに対してESモジュールを強制的に有効にするオプションを追加します。
"start": "node --experimental-specifier-resolution=node --loader tsx src/index.ts",
とのことです。
ただ--loaderオプションが非推奨になっていてエラーが出たため以下の記述に落ち着きました。
"start": "node --experimental-specifier-resolution=node --import tsx src/index.ts"
{
"name": "my-app",
"type": "module",
"scripts": {
"dev": "tsx watch src/index.ts"
},
"dependencies": {
"@hono/node-server": "^1.13.5",
"hono": "^4.6.9"
},
"devDependencies": {
"@types/node": "^20.11.17",
"tsx": "^4.7.1"
}
}
↓変更後
{
"name": "hono-node",
"type": "module",
"scripts": {
"start": "node --experimental-specifier-resolution=node --import tsx src/index.ts",
"dev": "tsx watch src/index.ts"
},
"dependencies": {
"@hono/node-server": "^1.13.4",
"hono": "^4.6.8"
},
"devDependencies": {
"@types/node": "^20.11.17",
"tsx": "^4.7.1"
}
}
ローカルで動くけどAzure Web Appsでtsxがうまく動かない問題はあるみたいですね。
Web Apps上のnodeコマンドはES ModulesやTypeScriptに対して特別な処理をしない限り、tsxを正しく解釈できないぽいです。(たぶん)
まとめ
この設定でローカルでもWeb Apps上でもHonoが動いてくれました。
ここから複雑にしていったときにまた別の問題あるかもですが一旦は動いてくれてます。