HonoをAWS Lambdaで動かすには？ローカル開発の2つの方法を解説！

# ローカル環境での起動方法が分からない :thinking: 

2024年12月14日時点、Hono の公式ドキュメントを見ると、[Cloudflare Workers](https://hono.dev/docs/getting-started/cloudflare-workers) には `npm run dev` でローカル環境で起動する方法が記載してありますが、[AWS Lambda](https://hono.dev/docs/getting-started/aws-lambda) には同等の記載がありません。

では、どうやってローカル環境で開発をすればよいのでしょうか？

この記事では、HonoをAWS Lambdaで動かす際の開発方法として、SAMを使う方法とNode.jsでのローカル実行方法の2つを紹介します！

:::note warn
試行錯誤中なので、コメントで教えていただけたら嬉しいです:bow:
:::

# 解決策

## 一般的な解決策（SAM） 

まず、一般的な解決策として AWS SAM (Serverless Application Model: 以下SAM)が挙げられます。
SAM はサーバーレスアプリケーションを簡単に作成・デプロイできるツールで、Lambda のデプロイだけではなく、API Gateway や IAM などのリソースも作成できて便利です。

反面、公式ドキュメントで紹介されている AWS Cloud Development Kit (AWS CDK) との併用は難しいかなと思いました。CDK は IaC であり、プロジェクト内のリソース管理の責務の線引きが難しいからです。

しかし、CDK にこだわらないのであれば、SAM の選択肢は良いと思います。

[実際のサンプルは GitHub にアップロードしてありますので、実装はそちらのほうでご覧ください。](https://github.com/Makohan/hono-aws-lambda/tree/sam-hono)

### ローカル実行（SAM)

SAM を導入し、ビルド&ローカル実行すると下記のように表示されます。

```bash
# ビルド
$ sam build --manifest package.json

Build Succeeded

# ローカル実行
$ sam local invoke

Invoking index.handler (nodejs20.x)                                                                                               
Local image is up-to-date                                                                                                         
Using local image: public.ecr.aws/lambda/nodejs:20-rapid-x86_64.                                                                  
                                                                                                                                  
Mounting /my-app/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated, inside runtime container  
START RequestId: 7c085261-433a-4a36-8efb-b7136dbc507b Version: $LATEST
END RequestId: 5db9c937-7b3b-47ae-b690-14360868af70
REPORT RequestId: 5db9c937-7b3b-47ae-b690-14360868af70  Init Duration: 0.08 ms  Duration: 150.66 ms     Billed Duration: 151 ms Memory Size: 128 MB       Max Memory Used: 128 MB
{"body": "Hello Hono!", "headers": {"content-type": "text/plain;charset=UTF-8"}, "statusCode": 200, "isBase64Encoded": false}
```

ビルド時に `public.ecr.aws/lambda/nodejs:20-rapid-x86_64` ベースの Docker Image が作られ、ローカル実行時に使われるので、実際のランタイム環境で動かすことができます。

## アプリケーションでの解決策（Hono の Node.js Adapterで動かす）

ローカル開発では、先ほど紹介した SAM を使った方法が推奨されると思います。
一方で、ネイティブモジュールやバイナリ依存のライブラリを使用しないのであれば、同じ Node.js のバージョンで動かす方法もできそうです。

Hono はあらゆる環境で動かすことができます。公式ドキュメントには [Node.js](https://hono.dev/docs/getting-started/nodejs) ランタイムがあり、そこには `npm run dev` を使ったローカル環境での実行方法も紹介されています。

これを応用して、実際の Lambda でのエントリーポイントとは別に、Node.js からのエントリーポイントも用意することができます。

### 構築手順

[実際のサンプルは GitHub にアップロードしてありますので、実装はそちらのほうでご覧ください。](https://github.com/Makohan/hono-aws-lambda/tree/nodejs-hono)

まず、Node.js Adapter と TypeScript をビルドするために tsx をインストールし、package.json に npm スクリプトを追加します。

```bash
npm install -D @hono/node-server tsx
```

```diff_json:package.json
  "scripts": {
+   "dev": "tsx watch lambda/local.ts",
    "build": "tsc",
    "watch": "tsc -w",
    "test": "jest",
    "cdk": "cdk"
  },

```

次に、index.ts に含まれる Hono の設定を app.ts に切り出します。

```typescript:app.ts
import { Hono } from "hono";

const app = new Hono()

app.get('/', (c) => c.text('Hello Hono!おめでとうございます！'))  // 確認用

export default app;
```

```index.ts
import { handle } from 'hono/aws-lambda'
import app from './app'

export const handler = handle(app)
```

そして、ローカル実行のエントリーポイントである local.ts を追加します。

```typescript:local.ts
import { serve } from '@hono/node-server'
import app from './app'

const port = 3000
console.log(`Server is running on http://localhost:${port}`)

serve({
  fetch: app.fetch,
  port
})
```

index.ts も local.ts も同じ app を参照していることが分かります。
この状態で `npm run dev` コマンドを実行すると、何ということでしょう。Node サーバーが起動しました :rocket:

```bash
$ npm run dev

> my-app@0.1.0 dev
> tsx watch lambda/local.ts

Server is running on http://localhost:3000
```

`http://localhost:3000/` にアクセスしてみると、先程 app.ts に書いた出力結果が表示されていることが分かります。

![変更が確認できる出力結果になっているページの画面キャプチャです](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134221/5e2a6756-d3f9-45e6-40d8-91557e56b10a.png)

Node サーバーは起動しているので、curl だけでなく、Postman などの GUI も使えますね！

### Node サーバーで動かすことのメリットとデメリット

Hono のアダプターを切り替えるだけで、簡単に Node サーバーが手に入ることが分かりました。
個人的に下記のメリットとデメリットがあると思います。

:::note
# メリット
- Node サーバーが起動しているので、すぐに API を実行できる
- 開発者が好きなツールで動作確認できる
:::

一方、デメリットもあります。

:::note warn
# デメリット
- Lambda の Node ランタイムではないので、ローカル環境と違う挙動になる可能性がある
  - ネイティブモジュールやバイナリ依存のライブラリを入れる際に調整が必要
:::

これらはアプリケーションの特性にも左右されるので、チームで話し合ったほうが良さそうですね。

# おわりに

Hono は素晴らしいフレームワークですが、みなさんどうやって開発されているのでしょうか？
開発から運用までノウハウが広まれば、もっと Hono の導入事例が増えると思います。
駆け出し Hono エンジニアの私に、みなさんのお知恵を何卒よろしくお願いします :bow: