KINGONEタッチペン【全機種対応 超高速充電】 2025業界最高 スタイラスペン 超高精度 1MM極細 アップルペンシル互換ペン 誤ON/OFF防止/電量表示/傾き検知/二重磁気吸着機能対応 iPad用ペンシル 軽量 耐摩 耐久 iPad/iPhone/Android/スマホ/タブレット対応タッチペン (ホワイト)
¥1,798 (2025年5月5日 13:17 GMT +09:00 時点 - 詳細はこちら価格および発送可能時期は表示された日付/時刻の時点のものであり、変更される場合があります。本商品の購入においては、購入の時点で当該の Amazon サイトに表示されている価格および発送可能時期の情報が適用されます。)usb c ケーブル【1m+1m+2m+2m 4本】タイプc ケーブル Type C 3A急速充電 QC3.0対応 急速充電 PD対応 ナイロン編み 断線防止iPhone 16/15 Samsung/Note/Huawei Sony Xperia Nintendo Switch/GoPro その他Android USB-C機器対応
¥799 (2025年5月5日 13:15 GMT +09:00 時点 - 詳細はこちら価格および発送可能時期は表示された日付/時刻の時点のものであり、変更される場合があります。本商品の購入においては、購入の時点で当該の Amazon サイトに表示されている価格および発送可能時期の情報が適用されます。)

バックエンドをAPI Gatewayに任せる時にめんどくさいのが、「複数のパスの管理」です。
各パスに対応するlambdaを書いたり、また、IaCのコード上で各パスをだらっと定義しまくったり・・・(for_eachなど工夫の余地はあると思いますが)。
そういうの面倒なので、今回はLambdalith
という手法を試してみます。
Lambdalithとは、1つのlambdaで複数のルート(パス)を処理しようというlambdaの設計スタイルです!
簡単にLambdalithを実現できるhonoというライブラリを使って、楽に実装してみます!!
※参考
Lambdalithについてまとまっている読みやすい記事です
アーキ
今回のアーキです。
API Gatewayと、lambdaが1つです!
Lambdalithを実現するために、lambda上のコードはhono
で実装します。
honoについては後述します。とりあえずドキュメント貼っておきます。
リポジトリ
参考になるかわかりませんが、今回作業したリポジトリです。
リポジトリの概要は以下
①honoでlambdaのコードを準備する
まずは今回の主役であるhonoというライブラリでlambdaを実装していきます!
honoのプロジェクトを準備
package.json
{
"name": "function",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "esbuild src/handler.ts --bundle --platform=node --outdir=dist"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"hono": "^4.7.8"
},
"devDependencies": {
"@hono/node-server": "^1.14.1",
"esbuild": "^0.25.3",
"tsx": "^4.19.4"
}
}
上記package.json
を参照し、pnpm i
していきます。
登場するライブラリを軽く紹介します。
hono
この記事の主役です。
REST APIサーバーを簡単に構築できるnodejsのライブラリです!!
ポータビリティが売りの一つで、今回やるように、lambdaなどにも簡単にのせることができます!!!
他にもいろいろ凄いです。やばいっす。
日本語版もあるくらい公式Docがフレンドリーで、よくまとめてくれています(AI向けのマークダウン形式ページもあったりしてそこも良い)。
この記事では紹介しきれないので、ぜひ読んでみてください。
@hono/node-server
honoをローカルに立てるためのライブラリです。
esbuild
tsをjsにトランスパイルするライブラリです。tscより速いらしいです。
tsx
tsを実行するライブラリです。ts-nodeより速いらしいです。
色々なルートをさばくhonoインスタンスを準備
では早速honoでREST APIを作っていきます。
中身はなんでもいいので、公式Docからコピペして以下のような構成にしました。
.
└── function
├── package.json
└── src
├── app.ts
├── handler.ts
├── index.ts
└── routes
├── book.ts
└── user.ts
1つずつ見ていきます!
src/routes/book.ts
APIGatewayの「/book」パスに一致するリクエストをさばくAPIを定義します。
複数のルーティングをまとめる場所で「/book」に対応させます。
src/routes/book.ts
import { Hono } from 'hono'
const book = new Hono()
book.get('/', (c) => c.text('List Books')) // GET /book
book.get('/:id', (c) => {
// GET /book/:id
const id = c.req.param('id')
return c.text('Get Book: ' + id)
})
book.post('/', (c) => c.text('Create Book')) // POST /book
export default book
src/routes/user.ts
src/routes/book.ts
と同じです。置換しただけですね。
src/routes/user.ts
import { Hono } from 'hono'
const user = new Hono()
user.get('/', (c) => c.text('List Users')) // GET /user
user.get('/:id', (c) => {
// GET /user/:id
const id = c.req.param('id')
return c.text('Get User: ' + id)
})
user.post('/', (c) => c.text('Create User')) // POST /user
export default user
src/app.ts
上記で作成した複数のルーティングインスタンスを統合するファイルです。
ルートが増えるたびにここに追加していくイメージです。
src/app.ts
import { Hono } from 'hono'
import book from './routes/book'
import user from './routes/user'
const app = new Hono()
app.route('/book', book)
app.route('/user', user)
export default app
src/handler.ts
上記で作成した、メインのインスタンスをlambda用のラッパーでつつんでexportするファイルです。
記載量の少なさがやばいですね。lambdaにはこのコードを載せます。これだけで良いのがビックリです。
src/handler.ts
import { handle } from 'hono/aws-lambda'
import app from './app'
export const handler = handle(app)
src/index.ts
lambdaには直接は不要です。
テスト用にlocalhostで上記のsrc/app.tsをREST APIサーバーとしてたてるためのファイルです。
src/index.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
})
package.json
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "esbuild src/handler.ts --bundle --platform=node --outdir=dist"
},
"dev": "tsx watch src/index.ts",
これで動きます。
> pnpm run dev
Server is running on http://localhost:3000
動かしてlocalでテストします!
>curl localhost:3000/user
List Users
>curl localhost:3000/user/1234
Get User: 1234
>curl -X POST localhost:3000/user
Create User
>curl -X POST localhost:3000/book
Create Book
>
良い感じですね!!
jsへトランスパイル
紹介したesbuildでトランスパイルします。
package.json
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "esbuild src/handler.ts --bundle --platform=node --outdir=dist"
},
> pnpm run build
> function@1.0.0 build C:/work/repo/hono-aws-hello-world/function
> esbuild src/handler.ts --bundle --platform=node --outdir=dist
dist/handler.js 55.1kb
Done in 71ms
爆速!!!
Lambda + API Gatewayを準備する
terraformでさっくりと
リソースの作成自体はterraformでさくっと終わらせます。
上記jsをのせたlambdaと、空のAPI Gatewayリソースを作成します。
つなげるところだけ手でやりましょう!
main.tf (長いので略)
main.tf
############################################################################
## terraformブロック
############################################################################
terraform {
# Terraformのバージョン指定
required_version = "~> 1.7.0"
# Terraformのaws用ライブラリのバージョン指定
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.33.0"
}
}
}
############################################################################
## providerブロック
############################################################################
provider "aws" {
# リージョンを指定
region = "ap-northeast-1"
}
locals {
project = "hono_aws_hello_world"
dir_path = "${path.module}/../function/dist"
}
############################################################################
## lambda
############################################################################
/* lambda実行ロール */
# lambda用AWSマネージドポリシーを準備
# ロギング用ポリシードキュメント
data "aws_iam_policy_document" "logging" {
statement {
effect = "Allow"
actions = [
"logs:CreateLogStream",
"logs:PutLogEvents",
]
resources = ["${aws_cloudwatch_log_group.lambda.arn}:*"]
}
}
# ロギング用ポリシー
resource "aws_iam_policy" "logging" {
name = "lambda-logging-policy"
description = "IAM policy for Lambda to write logs to CloudWatch"
policy = data.aws_iam_policy_document.logging.json
}
# lambda assume用ポリシードキュメント
data "aws_iam_policy_document" "lambda_assume_role" {
statement {
effect = "Allow"
principals {
type = "Service"
identifiers = ["lambda.amazonaws.com"]
}
actions = ["sts:AssumeRole"]
}
}
# lambda用IAMロール作成
resource "aws_iam_role" "lambda_execution_role" {
name = "my-lambda-execution-role"
assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json
}
# lambda用IAMロールへポリシーをアタッチ
resource "aws_iam_role_policy_attachment" "lambda_logs_attach" {
role = aws_iam_role.lambda_execution_role.name
policy_arn = aws_iam_policy.logging.arn
}
# ロググループを作成
resource "aws_cloudwatch_log_group" "lambda" {
name = "/aws/lambda/${local.project}_lambda"
retention_in_days = 14
}
# zipを作成
data "archive_file" "lambda_my_function" {
type = "zip"
output_file_mode = "0666"
source_dir = local.dir_path
output_path = "${local.dir_path}.zip"
}
/* lambda関数 */
resource "aws_lambda_function" "lambda" {
function_name = "${local.project}_lambda"
role = aws_iam_role.lambda_execution_role.arn
runtime = "nodejs20.x" # TODO:Terraform古い
filename = data.archive_file.lambda_my_function.output_path
handler = "handler.handler"
logging_config {
log_format = "Text"
log_group = aws_cloudwatch_log_group.lambda.name
}
# Terraformに変更を無視させるため、lifecycle ルールを追加
lifecycle {
ignore_changes = [filename, source_code_hash]
}
}
# API Gateway
resource "aws_api_gateway_rest_api" "rest" {
name = "${local.project}_rest_api"
}
# lambdaをAPI Gatewayから実行できるように許可する
resource "aws_lambda_permission" "apigw_lambda" {
statement_id = "AllowExecutionFromAPIGateway"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.lambda.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_api_gateway_rest_api.rest.execution_arn}/*/*"
}
API GatewayとLambdaをくっつける
API Gatewayのプロキシリソース機能とは
複数のパスをAPI Gatewayに設定する場合、個々にメソッドを設定する必要があると思います。
ただ今回は、すべてのリクエストを1つのLambdaでまとめて処理したいので、APIGatewayの「プロキシリソース機能」を使って、全パスのリクエストを1つのLambda関数に集約する構成にしてみます。
マネコンから操作してみる
プロキシリソース作成画面へ
作成したAPI Gatewayリソースを表示し、「リソースを作成」をクリックします
プロキシリソースとして定義する
「プロキシのリソース」トグルをチェックします。
リソース名を設定する
リソース名が求められるので定義します。文字列に意味はないため、例示されている{proxy+}
を設定します。
設定は以上なので、「リソースを作成」をクリックします。
リソース作成結果を確認する
良い感じですね。
ただ、メソッドの統合タイプが設定なしになっています。
リクエストはキャッチしますが、そのリクエストの伝搬先が設定されていない状況です。
メソッド設定画面へ
「ANY」をクリックします。
バックエンドリソースが未定義であることを確認する
未定義ですね。
統合先設定画面へ
「統合を編集」をクリックします。
Lambdaとプロキシ統合するよう設定する
「Lambdaプロキシ統合」トグルをオンにします。
honoは単体でもREST APIとして必要なレスポンスヘッダーやbodyを定義可能なので、プロキシ統合でOKです。ここら辺も簡単で素晴らしいですね。
作成したLambdaを指定する
作成したlambdaをドロップダウンから選択して、「保存」をクリックします。
喜ぶ
設定が完了しました。これだけで全てのパスがさばけます!!!!
今までIaCの中でだらだらパスを定義していたのはなんだったのでしょうか、最高!!!!
動作確認する!
GETしてみる
>curl https://ilm77o6qdl.execute-api.ap-northeast-1.amazonaws.com/dev/user
List Users
>curl https://ilm77o6qdl.execute-api.ap-northeast-1.amazonaws.com/dev/user/1234
Get User: 1234
>
良い感じ!
POSTしてみる
>curl -X POST https://ilm77o6qdl.execute-api.ap-northeast-1.amazonaws.com/dev/user
Create User
>curl -X POST https://ilm77o6qdl.execute-api.ap-northeast-1.amazonaws.com/dev/book
Create Book
最高!
存在しないパスを指定してみる
>curl https://ilm77o6qdl.execute-api.ap-northeast-1.amazonaws.com/dev/test
404 Not Found
なんとなにもしていないのにfallbackなレスポンスも返却してくれます!優しい
バックエンドも作らないと!でもめんどくさい!
テストも書きたいとなるとコードは軽量でローカルで動くようにしたい!でもめんどくさい!
めんどくさい!けどlambdaへのデプロイとか、その後のAPI Gatewayとの統合とか、更にいうとそれらのIaC管理も楽にしたい!!でもめんどくさい!
このような色んな欲求から、今回はhonoを試してみました。めっちゃ楽しかったです!
Views: 0