diff options
-rw-r--r-- | packages/bun-lambda/README.md | 102 | ||||
-rwxr-xr-x | packages/bun-lambda/bun.lockb | bin | 174029 -> 199405 bytes | |||
-rwxr-xr-x | packages/bun-lambda/runtime.ts | 17 |
3 files changed, 69 insertions, 50 deletions
diff --git a/packages/bun-lambda/README.md b/packages/bun-lambda/README.md index 015a6e26c..1553714ae 100644 --- a/packages/bun-lambda/README.md +++ b/packages/bun-lambda/README.md @@ -4,7 +4,7 @@ A custom runtime layer that runs Bun on AWS Lambda. ## Setup -First, you will need to deploy the layer to your AWS account. Clone this repository and run the `publish-layer` script to get started. +First, you will need to deploy the layer to your AWS account. Clone this repository and run the `publish-layer` script to get started. Note: the `publish-layer` script also builds the layer. ```sh git clone git@github.com:oven-sh/bun.git @@ -13,6 +13,67 @@ bun install bun run publish-layer ``` +## Usage + +Once you publish the layer to your AWS account, you can create a Lambda function that uses the layer. + +### Step 1: Create a Bun Lambda handler function + +In addition to providing the Bun runtime itself, the Bun Lambda Layer also provides an event transformation so you can write your Bun function in a classic Bun server format. This allows you to also run your Lambda function as a local Bun server with `bun run <handler-name>.ts`. Here are some examples of how to write a Bun Lambda function: + +#### HTTP Event Example + +When an event is triggered from [API Gateway](https://docs.aws.amazon.com/lambda/latest/dg/services-apigateway.html), the layer transforms the event payload into a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request). This means you can test your Lambda function locally using `bun run`, without any code changes. + +```ts +export default { + async fetch(request: Request): Promise<Response> { + console.log(request.headers.get("x-amzn-function-arn")); + // ... + return new Response("Hello from Lambda!", { + status: 200, + headers: { + "Content-Type": "text/plain", + }, + }); + }, +}; +``` + +#### Non-HTTP Event Example + +For non-HTTP events — S3, SQS, EventBridge, etc. — the event payload is the body of the [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request). + +```ts +export default { + async fetch(request: Request): Promise<Response> { + const event = await request.json(); + // ... + return new Response(); + }, +}; +``` + +### Step 2: Build the Bun handler + +The final step is to upload your Bun handler. You can technically write the handler directly in the console if you wish, but if you want a full development environment, use the Bun toolkit. There are several ways you can choose to build and manage your artifacts, but follow these steps for a simple approach: + +1. Run `bun build <handler-entry>.[ts|js] --outfile /dist/handler.js` +2. Zip the `/dist` folder + +### Step 3: Create the Lambda function on AWS + +Once you've written your Lambda function, you need to configure a new Lambda function to use Bun. The following steps apply to configuring in the console, CloudFormation, CDK, Terraform, or any other configuration management option for AWS: + +1. Create the Lambda function +2. Set the Runtime to custom with Amazon Linux 2 +3. Set the handler to <handler-file-name>.fetch (e.g. if your bundled Bun handler is at `handler.js`, set the handler as `handler.fetch`) +4. Set the architecture to whichever architecture you configured when you built/deployed the Lambda Layer +5. Attach the Lambda Layer to your new function +6. Upload the zip file from step 2. You can do this in the console directly, upload to S3 and set that as the location for the handler file in Lambda, or use something like CDK to manage this for you. + +## API + ### `bun run build-layer` Builds a Lambda layer for Bun and saves it to a `.zip` file. @@ -51,42 +112,3 @@ bun run publish-layer -- \ --output /path/to/layer.zip \ --region us-east-1 ``` - -## Usage - -Once you publish the layer to your AWS account, you can create a Lambda function that uses the layer. - -Here's an example function that can run on Lambda using the layer for Bun: - -### HTTP events - -When an event is triggered from [API Gateway](https://docs.aws.amazon.com/lambda/latest/dg/services-apigateway.html), the layer transforms the event payload into a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request). This means you can test your Lambda function locally using `bun run`, without any code changes. - -```ts -export default { - async fetch(request: Request): Promise<Response> { - console.log(request.headers.get("x-amzn-function-arn")); - // ... - return new Response("Hello from Lambda!", { - status: 200, - headers: { - "Content-Type": "text/plain", - }, - }); - }, -}; -``` - -### Non-HTTP events - -For non-HTTP events — S3, SQS, EventBridge, etc. — the event payload is the body of the [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request). - -```ts -export default { - async fetch(request: Request): Promise<Response> { - const event = await request.json(); - // ... - return new Response(); - }, -}; -``` diff --git a/packages/bun-lambda/bun.lockb b/packages/bun-lambda/bun.lockb Binary files differindex d826da88d..0292066e6 100755 --- a/packages/bun-lambda/bun.lockb +++ b/packages/bun-lambda/bun.lockb diff --git a/packages/bun-lambda/runtime.ts b/packages/bun-lambda/runtime.ts index b4e08944d..ab03376f5 100755 --- a/packages/bun-lambda/runtime.ts +++ b/packages/bun-lambda/runtime.ts @@ -294,7 +294,6 @@ function formatBody(body?: string, isBase64Encoded?: boolean): string | null { } type HttpEventV1 = { - readonly version: "1.0"; readonly requestContext: { readonly requestId: string; readonly domainName: string; @@ -310,15 +309,12 @@ type HttpEventV1 = { }; function isHttpEventV1(event: any): event is HttpEventV1 { - return event.version === "1.0" && typeof event.requestContext === "object"; + return !event.Records && event.version !== "2.0" && event.version !== "0" && typeof event.requestContext === "object"; } function formatHttpEventV1(event: HttpEventV1): Request { const request = event.requestContext; const headers = new Headers(); - for (const [name, value] of Object.entries(event.headers)) { - headers.append(name, value); - } for (const [name, values] of Object.entries(event.multiValueHeaders ?? {})) { for (const value of values) { headers.append(name, value); @@ -327,9 +323,6 @@ function formatHttpEventV1(event: HttpEventV1): Request { const hostname = headers.get("Host") ?? request.domainName; const proto = headers.get("X-Forwarded-Proto") ?? "http"; const url = new URL(request.path, `${proto}://${hostname}/`); - for (const [name, value] of new URLSearchParams(event.queryStringParameters)) { - url.searchParams.append(name, value); - } for (const [name, values] of Object.entries(event.multiValueQueryStringParameters ?? {})) { for (const value of values ?? []) { url.searchParams.append(name, value); @@ -360,7 +353,7 @@ type HttpEventV2 = { }; function isHttpEventV2(event: any): event is HttpEventV2 { - return event.version === "2.0" && typeof event.requestContext === "object"; + return !event.Records && event.version === "2.0" && typeof event.requestContext === "object"; } function formatHttpEventV2(event: HttpEventV2): Request { @@ -389,6 +382,10 @@ function formatHttpEventV2(event: HttpEventV2): Request { }); } +function isHttpEvent(event: any): boolean { + return isHttpEventV1(event) || isHttpEventV2(event); +} + type WebSocketEvent = { readonly headers: Record<string, string>; readonly multiValueHeaders: Record<string, string[]>; @@ -538,7 +535,7 @@ class LambdaServer implements Server { statusCode: 200, }; } - if (!request?.headers.has("Host")) { + if (!isHttpEvent(event.event)) { return response.text(); } return formatResponse(response); |