LoginSignup
24
8

最近 Hono で外部連携用の AWS Lambda 書いています

Last updated at Posted at 2024-03-25

AWS Lambda のハンドラは辛い

こんにちは!AWS Lambda初心者です!

AWS Lambda と言えば、サーバレスかつ非常に安い価格で無限にスケールアウトできる処理を書けることで有名ですが12022年04月の Function URL アップデート でAPI Gatewayなど用いなくてもラムダ単体かつ無料でHTTPリクエストを処理できるURLを持てるようになり、かなりの神サービスになりました。

弊社でも Function URL でラムダによる外部連携用のサーバを立てておき、ちょっとしたHTTPのリクエストを受け取って、他所のサーバにデータ連携したり、S3にリクエスト中のデータを保存したりする用途に積極的に使っています。

さて、そんな神サービスである AWS Lambda 君ですが、いざ実際にJavaScriptでHTTPサーバを書こうとすると…… ちょっと辛い!

// https://lambda-function-url/echo にアクセスすると "Hello" を返すサーバの例

exports.handler = async (event) => {
    if (event.httpMethod === "GET" && event.path === "/echo") {
        return {
            statusCode: 200,
            body: "Hello"
        };
    }
    
    return {
        statusCode: 404
    };
};
  • event に何が入ってくるのかイマイチピンとこない
    • しかもAPIゲートウェイ経由か関数URL経由かによって渡されるものが違う
    • 渡されたオブジェクト内のいろんなフィールドに同じ値が入っているが、そのうちのどれを使っていいのか分からない
  • return何を返せばいいのか分からない
  • この仕様でAWS Lambdaにべったりベンダーロックインされたコードを書くのが嫌だ
    • いざとなったら自前でサーバを立てたり、類似の別サービスに乗り換えたい
  • 大規模なものを書くと詰みそう

というあまり良くない開発体験になりがちです。

そこで Hono !

そこで、最近は直接ラムダのハンドラを書くのではなく、 Hono を使って HTTP 用のラムダを書いています。

src/index.ts
import { Hono } from 'hono'

// 純粋なHTTPサーバの定義
const app = new Hono()

// Express.js のようなルーターを書ける 
app.get('/echo', async (c) => {
  return c.text("Hello")
})

export default app
src/lambda.ts
import { handle } from 'hono/aws-lambda'
import app from './index.ts'

// index.ts で定義された純粋なHTTPサーバをAWS Lambda用のアダプタでラップしてハンドラとしてエクスポート
export const handler = handle(app)

src/index.ts は純粋なHTTPサーバの定義で、 src/lambda.ts はそのHTTPサーバを AWS Lambda 用のアダプタでラップしたものになっています。ラムダのエントリポイントとして登録するのは src/lambda.ts になります。

ラムダを立てずに、ローカルでHTTPサーバを実行するには src/index.ts を起動すればよく、以下のコマンドで起動できます。
(以降Bunを実行していますが、Node.jsでも設定を行えば基本的に実行可能です)

$ bun run --hot src/index.ts
Started server http://localhost:3000

$ curl http://localhost:3000/echo
Hello

逆にAWS Lambda上で上記のサーバを起動するには ESBuild などでバンドル&zip化した src/lambda.ts をAWS LambdaにアップロードすればOKです。

# ESBuild でバンドル (外部のライブラリを参照していてもうまい具合に1ファイルにバンドルされる)
$ bun run esbuild --bundle \
    --outfile=./dist/index.js \
    --platform=node --target=node20 \
    ./src/lambda.ts

# zip化
$ zip -j lambda.zip dist/index.js

# zipをAWS Lambdaにアップロード
$ aws lambda update-function-code \
    --zip-file fileb://lambda.zip \
    --function-name my-lambda-function-on-aws

上記のコマンド・依存性の初期準備が難しい? 心配は無用です。各種JS処理系の create コマンドでプロジェクトの雛形を作れるようになっています。

$ bun create hono .

create-hono version 0.5.0
✔ Using target directory … .
✔ Which template do you want to use? › aws-lambda
cloned honojs/starter#main to /Users/ksaitou/hono-aws-lambda
✔ Do you want to install project dependencies? … yes
✔ Which package manager do you want to use? › bun
✔ Installed project dependencies

その他、Honoには便利機能が満載です。

AWS Lambda から Cloudflare Workers に夜逃げしてみる

では、実際にAWS Lambda用に作った前述のプロジェクトを Cloudflare Workers にアップロードして動かしてみたいと思います。

Cloudflare Workers のハンドラは以下のようなものなのですが、果たしてHonoはそのまま動くのでしょうか?

export default {
  async fetch(request, env, ctx) {
    return new Response('Hello World!');
  },
}

その前にいくつか準備が必要です。

wrangler.toml
# Cloudflare の設定ファイル
name = "WorkerのID"
compatibility_date = "2023-12-01"
# Cloudflare 関連の依存性の追加
$ bun add -D wrangler @cloudflare/workers-types

準備が出来たのでデプロイしてみます。

$ bun run wrangler deploy --minify src/index.ts
 ⛅️ wrangler 3.36.0
-------------------
Total Upload: 18.93 KiB / gzip: 7.17 KiB
Uploaded WorkerのID (1.40 sec)
Published WorkerのID (0.30 sec)
  https://WorkerのID.ksaitou.workers.dev
Current Deployment ID: WorkerのID

動かしてみましょう。

$ curl https://WorkerのID.ksaitou.workers.dev/echo
Hello

AWS Lambda 用に書いたサーバが Cloudflare Workers でもちゃんと動きました!

まとめ

Hono を使えばプラットフォーム可搬性のあるTypeScriptによるHTTPサーバを簡単に書くことができます。それぞれのプラットフォームの理解が難しいAPIに触れる必要もありません。

ちょっとした外部連携用のHTTPサーバを書く際など非常に便利なので使ってみましょう。

続編も書きました!

  1. 正直AWS内での広範な扱いを見ていると、AWSの便利なマクロ拡張関数といったほうが適切な気はします

24
8
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
8