diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 27138349..4792c34d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -187,6 +187,17 @@ updates: security: applies-to: security-updates patterns: ["*"] + - package-ecosystem: npm + directories: ["/examples/bun-hono/app"] + schedule: + interval: weekly + open-pull-requests-limit: 0 + commit-message: + prefix: chore + groups: + security: + applies-to: security-updates + patterns: ["*"] - package-ecosystem: npm directories: - "/examples/datadog/expressjs-streaming/cdk" diff --git a/.github/example-matrix.json b/.github/example-matrix.json index 6c4f8d97..91030884 100644 --- a/.github/example-matrix.json +++ b/.github/example-matrix.json @@ -1,5 +1,6 @@ { "image": [ + { "name": "bun-hono", "path": "/", "expect_body": "Hello from Hono" }, { "name": "expressjs", "path": "/", "expect_body": "Hi there!" }, { "name": "fastapi", "path": "/", "expect_body": "message" }, { "name": "fastapi-background-tasks", "path": "/", "expect_body": "message" }, diff --git a/README.md b/README.md index 67c2f2fb..39b7fdf4 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,7 @@ See the [FastAPI with SnapStart example](examples/fastapi-snapstart-zip) for a c - [FastHTML with Response Streaming in Zip](examples/fasthtml-response-streaming-zip) - [Remix](examples/remix/) - [Remix in Zip](examples/remix-zip/) +- [Bun Hono](examples/bun-hono) - [Sveltekit SSR Zip](examples/sveltekit-ssr-zip/) - [Datadog](examples/datadog) - [Datadog in Zip](examples/datadog-zip) diff --git a/docs/guide/src/examples/overview.md b/docs/guide/src/examples/overview.md index b050c732..4555e798 100644 --- a/docs/guide/src/examples/overview.md +++ b/docs/guide/src/examples/overview.md @@ -39,6 +39,7 @@ The repository includes working examples for many popular web frameworks, packag | [SvelteKit SSR Zip](https://github.com/aws/aws-lambda-web-adapter/tree/main/examples/sveltekit-ssr-zip) | Zip | No | | [Bun GraphQL](https://github.com/aws/aws-lambda-web-adapter/tree/main/examples/bun-graphql-zip) | Zip | No | | [Bun GraphQL Streaming](https://github.com/aws/aws-lambda-web-adapter/tree/main/examples/bun-graphql-streaming-zip) | Zip | Yes | +| [Bun Hono](https://github.com/aws/aws-lambda-web-adapter/tree/main/examples/bun-hono) | Docker | No | ## Java diff --git a/examples/bun-hono/.gitignore b/examples/bun-hono/.gitignore new file mode 100644 index 00000000..3ef28a96 --- /dev/null +++ b/examples/bun-hono/.gitignore @@ -0,0 +1,2 @@ +node_modules +.aws-sam diff --git a/examples/bun-hono/README.md b/examples/bun-hono/README.md new file mode 100644 index 00000000..dd2507fe --- /dev/null +++ b/examples/bun-hono/README.md @@ -0,0 +1,86 @@ +# Bun Hono example + +This example shows how to use Lambda Web Adapter to run a [Hono](https://hono.dev) application on [Bun](https://bun.sh) in a container image. + +The application is a Bun HTTP server in TypeScript. Bun runs the `index.ts` file directly, thus the application has no build step. The application has no Lambda-specific code. You can run the same image on Lambda, on Amazon ECS, or on your computer. + +The top level folder is an AWS SAM project. The `app` directory contains the application and its [Dockerfile](app/Dockerfile). + +This line of the Dockerfile copies the Lambda Web Adapter binary into `/opt/extensions`. It is the only change that Lambda needs. + +```dockerfile +COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:1.1.0 /lambda-adapter /opt/extensions/lambda-adapter +``` + +The examples in this repository get their base images from Amazon ECR Public. Bun has no official image there. Thus the Dockerfile starts from the Node.js image and installs Bun with npm. + +The application listens on the port that the `PORT` environment variable gives. Lambda Web Adapter uses the same variable to find the application. + +## Pre-requisites + +Install and configure these tools. + +* [AWS CLI](https://aws.amazon.com/cli/) +* [SAM CLI](https://github.com/aws/aws-sam-cli) +* [Docker](https://www.docker.com/products/docker-desktop) + +## Deploy to Lambda + +Go to the folder of this example. Use SAM CLI to build the container image. + +```shell +sam build +``` + +Deploy the application to your AWS account. SAM CLI asks you for the deployment settings. + +```shell +sam deploy --guided +``` + +When the deployment is complete, find the `BunHonoApi` output. Its value is the URL of the API Gateway endpoint. Send a request to that URL. + +```shell +curl https://.execute-api..amazonaws.com/ +``` + +```json +{"message":"Hello from Hono on Bun!","requestId":"","functionName":""} +``` + +Lambda Web Adapter sends the request context from API Gateway and the Lambda context to the application. It puts them in the `x-amzn-request-context` and `x-amzn-lambda-context` headers. The application reads the request ID from the first header and the function name from the second header. + +The two contexts contain your AWS account ID. Thus the application does not return the full contexts. + +The application also has a route with a path parameter. + +```shell +curl https://.execute-api..amazonaws.com/hello/lambda +``` + +```json +{"message":"Hello, lambda!"} +``` + +## Run the docker locally + +You can run the same image without Lambda. Build the image and start a container. + +```shell +docker build -t bun-hono app +docker run --rm -p 8000:8000 bun-hono +``` + +Then send a request from a second terminal. + +```shell +curl http://localhost:8000/ +``` + +## Clean up + +Delete the stack when you do not need the example. + +```shell +sam delete +``` diff --git a/examples/bun-hono/app/.dockerignore b/examples/bun-hono/app/.dockerignore new file mode 100644 index 00000000..3c3629e6 --- /dev/null +++ b/examples/bun-hono/app/.dockerignore @@ -0,0 +1 @@ +node_modules diff --git a/examples/bun-hono/app/Dockerfile b/examples/bun-hono/app/Dockerfile new file mode 100644 index 00000000..ba09f5b1 --- /dev/null +++ b/examples/bun-hono/app/Dockerfile @@ -0,0 +1,15 @@ +FROM public.ecr.aws/docker/library/node:22-slim +COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:1.1.0 /lambda-adapter /opt/extensions/lambda-adapter +# Bun has no official image on Amazon ECR Public, so install it with npm. +RUN npm install --global bun@1.4.2 +# Listen on 8000 (the app reads PORT) so it doesn't collide with SAM's Runtime +# Interface Emulator on 8080 during local testing. +ENV PORT=8000 +# The Lambda file system is read-only, so turn off Bun's transpiler cache. +ENV BUN_RUNTIME_TRANSPILER_CACHE_PATH=0 +EXPOSE 8000 +WORKDIR /var/task +COPY package.json bun.lock ./ +RUN bun install --frozen-lockfile --production +COPY index.ts ./ +CMD ["bun", "run", "index.ts"] diff --git a/examples/bun-hono/app/bun.lock b/examples/bun-hono/app/bun.lock new file mode 100644 index 00000000..0bbf8454 --- /dev/null +++ b/examples/bun-hono/app/bun.lock @@ -0,0 +1,26 @@ +{ + "lockfileVersion": 2, + "configVersion": 1, + "workspaces": { + "": { + "name": "bun-hono", + "dependencies": { + "hono": "^4.13.13", + }, + "devDependencies": { + "@types/bun": "^1.4.2", + }, + }, + }, + "packages": { + "@types/bun": ["@types/bun@1.4.2", "", { "dependencies": { "bun-types": "1.4.2" } }, "sha512-GimotNn7+ZV0uVArItBbriZsR1oNf0+WTzPkdcFrzShI7k2norL0uzEaJT8T33dWr7O/c9ZDuAFQrctKCi72oQ=="], + + "@types/node": ["@types/node@26.6.4", "", { "dependencies": { "undici-types": "~8.9.0" } }, "sha512-ldVPDCzj7fsaGZrLB0NuHuTvJcsNasysBAqMolr/cgxrLd1xbqxIr3XJiPnHHJUCxj5sNF1vnRj9aWnrVh5Jcg=="], + + "bun-types": ["bun-types@1.4.2", "", { "dependencies": { "@types/node": "*" } }, "sha512-bxV1FgK7yBIzjRe5zBozIM4Bem11ZJcCXSrjWRG3YWLt8yFDePu4cLjpebO8OvPeIE9trbyPF4fuj3Cia4Fj3w=="], + + "hono": ["hono@4.13.13", "", {}, "sha512-CQ46U0ZkAGmbT/4UxdzzGJpacP2IeKgY4a5/tOI9AABbpOMfK739wfDXmv1usCk+3RkKj1hQy4/fjhiwa2xlrA=="], + + "undici-types": ["undici-types@8.9.0", "", {}, "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg=="], + } +} diff --git a/examples/bun-hono/app/index.ts b/examples/bun-hono/app/index.ts new file mode 100644 index 00000000..9ec0c066 --- /dev/null +++ b/examples/bun-hono/app/index.ts @@ -0,0 +1,33 @@ +import { Hono } from 'hono' + +const app = new Hono() +const port = Number(process.env.PORT || 8080) + +// Lambda Web Adapter forwards the API Gateway request context and the Lambda +// context to the app as JSON strings in two HTTP headers. +const parseContext = (header?: string) => (header ? JSON.parse(header) : null) + +app.get('/', (c) => { + const requestContext = parseContext(c.req.header('x-amzn-request-context')) + const lambdaContext = parseContext(c.req.header('x-amzn-lambda-context')) + + // Return two fields only. The full contexts contain the AWS account ID. + return c.json({ + message: 'Hello from Hono on Bun!', + requestId: requestContext?.requestId ?? null, + functionName: lambdaContext?.env_config?.function_name ?? null, + }) +}) + +app.get('/hello/:name', (c) => { + return c.json({ message: `Hello, ${c.req.param('name')}!` }) +}) + +const server = Bun.serve({ port, fetch: app.fetch }) +console.log(`Hono app listening on port ${server.port}`) + +// Lambda sends SIGTERM before it shuts down the execution environment. +process.on('SIGTERM', async () => { + await server.stop() + process.exit(0) +}) diff --git a/examples/bun-hono/app/package.json b/examples/bun-hono/app/package.json new file mode 100644 index 00000000..937916e0 --- /dev/null +++ b/examples/bun-hono/app/package.json @@ -0,0 +1,16 @@ +{ + "name": "bun-hono", + "version": "1.0.0", + "description": "Hono on Bun example for AWS Lambda Web Adapter", + "private": true, + "type": "module", + "scripts": { + "start": "bun run index.ts" + }, + "dependencies": { + "hono": "^4.13.13" + }, + "devDependencies": { + "@types/bun": "^1.4.2" + } +} diff --git a/examples/bun-hono/app/tsconfig.json b/examples/bun-hono/app/tsconfig.json new file mode 100644 index 00000000..2dd47e31 --- /dev/null +++ b/examples/bun-hono/app/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "Preserve", + "moduleResolution": "bundler", + "types": ["bun"], + "strict": true, + "skipLibCheck": true, + "noEmit": true + } +} diff --git a/examples/bun-hono/template.yaml b/examples/bun-hono/template.yaml new file mode 100644 index 00000000..ba32d13c --- /dev/null +++ b/examples/bun-hono/template.yaml @@ -0,0 +1,32 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + bun-hono + + Sample SAM Template for bun-hono + +# More info about Globals: https://github.com/aws/serverless-application-model/blob/master/docs/globals.rst +Globals: + Function: + Timeout: 10 + +Resources: + BunHonoFunction: + Type: AWS::Serverless::Function + Properties: + PackageType: Image + MemorySize: 512 + Architectures: + - x86_64 + Events: + HttpEvents: + Type: HttpApi + Metadata: + DockerTag: v1 + DockerContext: ./app + Dockerfile: Dockerfile + +Outputs: + BunHonoApi: + Description: "API Gateway endpoint URL for Bun Hono function" + Value: !Sub "https://${ServerlessHttpApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/"