AWS Lambda

AWS Lambda 是 Amazon Web Services 提供的无服务器(serverless)平台。你可以在响应事件时运行代码,并且底层的计算资源会自动管理。

Hono 可以运行在 Node.js 18+ 环境的 AWS Lambda 上。

1. 设置

在 AWS Lambda 上创建应用时,可以使用 CDK 来设置函数、IAM Role、API Gateway 等资源。

使用 cdk CLI 初始化项目。

npm

  1. mkdir my-app
  2. cd my-app
  3. cdk init app -l typescript
  4. npm i hono
  5. npm i -D esbuild
  6. mkdir lambda
  7. touch lambda/index.ts

yarn

  1. mkdir my-app
  2. cd my-app
  3. cdk init app -l typescript
  4. yarn add hono
  5. yarn add -D esbuild
  6. mkdir lambda
  7. touch lambda/index.ts

pnpm

  1. mkdir my-app
  2. cd my-app
  3. cdk init app -l typescript
  4. pnpm add hono
  5. pnpm add -D esbuild
  6. mkdir lambda
  7. touch lambda/index.ts

bun

  1. mkdir my-app
  2. cd my-app
  3. cdk init app -l typescript
  4. bun add hono
  5. bun add -D esbuild
  6. mkdir lambda
  7. touch lambda/index.ts

2. Hello World

编辑 lambda/index.ts

  1. import { Hono } from 'hono'
  2. import { handle } from 'hono/aws-lambda'
  3. const app = new Hono()
  4. app.get('/', (c) => c.text('Hello Hono!'))
  5. export const handler = handle(app)

3. 部署

编辑 lib/my-app-stack.ts

  1. import * as cdk from 'aws-cdk-lib'
  2. import { Construct } from 'constructs'
  3. import * as lambda from 'aws-cdk-lib/aws-lambda'
  4. import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'
  5. export class MyAppStack extends cdk.Stack {
  6. constructor(scope: Construct, id: string, props?: cdk.StackProps) {
  7. super(scope, id, props)
  8. const fn = new NodejsFunction(this, 'lambda', {
  9. entry: 'lambda/index.ts',
  10. handler: 'handler',
  11. runtime: lambda.Runtime.NODEJS_22_X,
  12. })
  13. const fnUrl = fn.addFunctionUrl({
  14. authType: lambda.FunctionUrlAuthType.NONE,
  15. })
  16. new cdk.CfnOutput(this, 'lambdaUrl', {
  17. value: fnUrl.url!
  18. })
  19. }
  20. }

最后执行命令进行部署:

  1. cdk deploy

返回二进制数据

Hono 支持以响应形式返回二进制数据。在 Lambda 中,返回二进制数据时需要进行 base64 编码。一旦将 Content-Type 头设置为二进制类型,Hono 会自动将数据编码为 base64。

  1. app.get('/binary', async (c) => {
  2. // ...
  3. c.status(200)
  4. c.header('Content-Type', 'image/png') // 表示返回二进制数据
  5. return c.body(buffer) // 支持 `ArrayBufferLike` 类型,自动编码为 base64
  6. })

访问 AWS Lambda 对象

在 Hono 中,你可以通过绑定 LambdaEventLambdaContext 类型并使用 c.env 来访问 AWS Lambda 的 Events 和 Context。

  1. import { Hono } from 'hono'
  2. import type { LambdaEvent, LambdaContext } from 'hono/aws-lambda'
  3. import { handle } from 'hono/aws-lambda'
  4. type Bindings = {
  5. event: LambdaEvent
  6. lambdaContext: LambdaContext
  7. }
  8. const app = new Hono<{ Bindings: Bindings }>()
  9. app.get('/aws-lambda-info/', (c) => {
  10. return c.json({
  11. isBase64Encoded: c.env.event.isBase64Encoded,
  12. awsRequestId: c.env.lambdaContext.awsRequestId,
  13. })
  14. })
  15. export const handler = handle(app)

访问 RequestContext

在 Hono 中,你可以通过绑定 LambdaEvent 类型并使用 c.env.event.requestContext 来访问 AWS Lambda 的请求上下文。

  1. import { Hono } from 'hono'
  2. import type { LambdaEvent } from 'hono/aws-lambda'
  3. import { handle } from 'hono/aws-lambda'
  4. type Bindings = {
  5. event: LambdaEvent
  6. }
  7. const app = new Hono<{ Bindings: Bindings }>()
  8. app.get('/custom-context/', (c) => {
  9. const lambdaContext = c.env.event.requestContext
  10. return c.json(lambdaContext)
  11. })
  12. export const handler = handle(app)

v3.10.0 之前版本(已弃用)

在旧版本中,可以通过绑定 ApiGatewayRequestContext 类型并使用 c.env 来访问请求上下文。

  1. import { Hono } from 'hono'
  2. import type { ApiGatewayRequestContext } from 'hono/aws-lambda'
  3. import { handle } from 'hono/aws-lambda'
  4. type Bindings = {
  5. requestContext: ApiGatewayRequestContext
  6. }
  7. const app = new Hono<{ Bindings: Bindings }>()
  8. app.get('/custom-context/', (c) => {
  9. const lambdaContext = c.env.requestContext
  10. return c.json(lambdaContext)
  11. })
  12. export const handler = handle(app)

Lambda 响应流(Streaming)

通过修改 AWS Lambda 的调用模式,可以实现 Streaming Response

  1. fn.addFunctionUrl({
  2. authType: lambda.FunctionUrlAuthType.NONE,
  3. + invokeMode: lambda.InvokeMode.RESPONSE_STREAM,
  4. })

通常,实现流式响应需要使用 awslambda.streamifyResponseNodeJS.WritableStream 写入数据。但使用 AWS Lambda Adaptor 后,你可以直接使用 Hono 的传统流式响应,只需使用 streamHandle 替代 handle

  1. import { Hono } from 'hono'
  2. import { streamHandle } from 'hono/aws-lambda'
  3. import { streamText } from 'hono/streaming'
  4. const app = new Hono()
  5. app.get('/stream', async (c) => {
  6. return streamText(c, async (stream) => {
  7. for (let i = 0; i < 3; i++) {
  8. await stream.writeln(`${i}`)
  9. await stream.sleep(1)
  10. }
  11. })
  12. })
  13. export const handler = streamHandle(app)