AWS Lambda
AWS Lambda は Amazon Web Services が提供するサーバーレスプラットフォームです。 イベントに応じてコードを実行でき、基盤となるコンピューティングリソースは自動的に管理されます。
Hono は Node.js 18 以降の AWS Lambda 環境で動作します。
1. セットアップ
AWS Lambda 上でアプリケーションを作成する際には、 CDK を使うと、IAM ロールや API Gateway などを設定するのに便利です。
cdk CLI でプロジェクトを初期化します。
mkdir my-app
cd my-app
cdk init app -l typescript
npm i hono @hono/aws-lambda
npm i -D esbuild
mkdir lambda
touch lambda/index.tsmkdir my-app
cd my-app
cdk init app -l typescript
yarn add hono @hono/aws-lambda
yarn add -D esbuild
mkdir lambda
touch lambda/index.tsmkdir my-app
cd my-app
cdk init app -l typescript
pnpm add hono @hono/aws-lambda
pnpm add -D esbuild
mkdir lambda
touch lambda/index.tsmkdir my-app
cd my-app
cdk init app -l typescript
bun add hono @hono/aws-lambda
bun add -D esbuild
mkdir lambda
touch lambda/index.ts2. Hello World
lambda/index.ts を編集します。
import { Hono } from 'hono'
import { handle } from '@hono/aws-lambda'
const app = new Hono()
app.get('/', (c) => c.text('Hello Hono!'))
export const handler = handle(app)3. デプロイ
lib/my-app-stack.ts を編集します。
import * as cdk from 'aws-cdk-lib'
import { Construct } from 'constructs'
import * as lambda from 'aws-cdk-lib/aws-lambda'
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'
export class MyAppStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props)
const fn = new NodejsFunction(this, 'lambda', {
entry: 'lambda/index.ts',
handler: 'handler',
runtime: lambda.Runtime.NODEJS_22_X,
})
const fnUrl = fn.addFunctionUrl({
authType: lambda.FunctionUrlAuthType.NONE,
})
new cdk.CfnOutput(this, 'lambdaUrl', {
value: fnUrl.url!,
})
}
}最後に、次のコマンドを実行してデプロイします。
cdk deployバイナリデータの返却
Hono はレスポンスとしてバイナリデータを返すことができます。 Lambda でバイナリデータを返すには、base64 エンコードが必要です。 Content-Type ヘッダーにバイナリの種類を設定すると、Hono はデータを自動的に base64 にエンコードします。
app.get('/binary', async (c) => {
// ...
c.status(200)
c.header('Content-Type', 'image/png') // means binary data
return c.body(buffer) // supports `ArrayBufferLike` type, encoded to base64.
})AWS Lambda オブジェクトへのアクセス
Hono では、LambdaEvent、LambdaContext 型をバインドし、c.env を使うことで AWS Lambda のイベントとコンテキストにアクセスできます。
import { Hono } from 'hono'
import type { LambdaEvent, LambdaContext } from '@hono/aws-lambda'
import { handle } from '@hono/aws-lambda'
type Bindings = {
event: LambdaEvent
lambdaContext: LambdaContext
}
const app = new Hono<{ Bindings: Bindings }>()
app.get('/aws-lambda-info/', (c) => {
return c.json({
isBase64Encoded: c.env.event.isBase64Encoded,
awsRequestId: c.env.lambdaContext.awsRequestId,
})
})
export const handler = handle(app)RequestContext へのアクセス
Hono では、LambdaEvent 型をバインドし、c.env.event.requestContext を使うことで AWS Lambda のリクエストコンテキストにアクセスできます。
import { Hono } from 'hono'
import type { LambdaEvent } from '@hono/aws-lambda'
import { handle } from '@hono/aws-lambda'
type Bindings = {
event: LambdaEvent
}
const app = new Hono<{ Bindings: Bindings }>()
app.get('/custom-context/', (c) => {
const lambdaContext = c.env.event.requestContext
return c.json(lambdaContext)
})
export const handler = handle(app)v3.10.0 より前のバージョン(非推奨)
ApiGatewayRequestContext 型をバインドし、c.env. を使うことで AWS Lambda のリクエストコンテキストにアクセスできます。
import { Hono } from 'hono'
import type { ApiGatewayRequestContext } from '@hono/aws-lambda'
import { handle } from '@hono/aws-lambda'
type Bindings = {
requestContext: ApiGatewayRequestContext
}
const app = new Hono<{ Bindings: Bindings }>()
app.get('/custom-context/', (c) => {
const lambdaContext = c.env.requestContext
return c.json(lambdaContext)
})
export const handler = handle(app)Lambda レスポンスストリーミング
AWS Lambda の呼び出しモードを変更すると、ストリーミングレスポンス を実現できます。
fn.addFunctionUrl({
authType: lambda.FunctionUrlAuthType.NONE,
+ invokeMode: lambda.InvokeMode.RESPONSE_STREAM,
})通常は awslambda.streamifyResponse を使い、NodeJS.WritableStream にチャンクを書き込む必要があります。AWS Lambda アダプターでは、handle の代わりに streamHandle を使うことで、Hono の通常のストリーミングレスポンスを実現できます。
import { Hono } from 'hono'
import { streamHandle } from '@hono/aws-lambda'
import { streamText } from 'hono/streaming'
const app = new Hono()
app.get('/stream', async (c) => {
return streamText(c, async (stream) => {
for (let i = 0; i < 3; i++) {
await stream.writeln(`${i}`)
await stream.sleep(1)
}
})
})
export const handler = streamHandle(app)