Bun is a toolkit for JavaScript and TypeScript applications, packaged as a single executable named “bun.” It acts as a runtime, manages packages, bundles code, and runs tests, ensuring a smooth and effective development workflow. Developed in Zig and powered by JavaScriptCore, it drastically cuts down startup times and memory consumption.
Three things stand out to me:
All-in-One Solution: Bun provides a toolkit, including a runtime, package manager, bundler, and test runner. If you prefer having a cohesive and integrated development environment without the need to integrate multiple tools separately, Bun can be a convenient choice.
Performance Optimization: Bun is designed with a focus on performance. If you are working on projects where performance optimization is a critical concern, and you need a runtime and development environment that is tuned for speed, Bun might offer advantages over the default Node.js setup.
Simplified Development: Bun aims to simplify the development process, making it more straightforward and efficient. If you are looking for a tool that abstracts away complexities and provides a more streamlined workflow, Bun could be a suitable option.
Let’s continue with the installation and setup for the AWS Lambda environment.
For having the bun cli tool installed we can use Brew or something similar on different operating systems.
❯ brew install bun
==> Downloading https://formulae.brew.sh/api/formula.jws.json
################ 100.0%
==> Downloading https://formulae.brew.sh/api/cask.jws.json
################ 100.0%
==> Fetching oven-sh/bun/bun
==> Summary
🍺 /opt/homebrew/Cellar/bun/1.0.6: 7 files, 48.8MB, built in 2 seconds
==> Running `brew cleanup bun`...
Running it without any parameter:
❯ bun
Bun: a fast JavaScript runtime, package manager, bundler and test runner. (1.0.6)
run ./my-script.ts Run JavaScript with Bun, a package.json script, or a bin
test Run unit tests with Bun
x bun-repl Install and execute a package bin (bunx)
repl Start a REPL session with Bun
init Start an empty Bun project from a blank template
create astro Create a new project from a template (bun c)
install Install dependencies for a package.json (bun i)
add zod Add a dependency to package.json (bun a)
remove redux Remove a dependency from package.json (bun rm)
update tailwindcss Update outdated dependencies
link Link an npm package globally
unlink Globally unlink an npm package
pm More commands for managing packages
build ./a.ts ./b.jsx Bundle TypeScript & JavaScript into a single file
upgrade Get the latest version of Bun
bun --help Show all supported flags and commands
Learn more about Bun: https://bun.sh/docs
Join our Discord community: https://bun.sh/discord
This step is needed to build a Lambda layer.
❯ git clone git@github.com:oven-sh/bun.git
Cloning into 'bun'...
❯ cd bun/
❯ git checkout bun-v1.0.6
Note: switching to 'bun-v1.0.6'.
HEAD is now at 969da088f fix(install): re-evaluate overrides when removed
bun (969da08) via 🍞 v1.0.1 via ↯ v0.10.1
There are a few dependencies that need to be installed before we can create a Lambda layer.
❯ cd packages/bun-lambda
❯ bun install
bun install v1.0.6 (969da088)
+ @oclif/plugin-plugins@3.9.1
+ bun-types@0.7.3
+ jszip@3.10.1
+ oclif@3.6.5
+ prettier@2.8.4
+ aws4fetch@1.0.17
686 packages installed [6.68s]
❯ bun install @oclif/plugin-plugins
bun add v1.0.6 (969da088)
installed @oclif/plugin-plugins@3.9.1
4 packages installed [110.00ms]
With the dependencies installed we can create the Lambda layer for arm64.
❯ bun run build-layer -- \
--arch aarch64 \
--release latest \
--output ./bun-lambda-layer.zip
$ bun scripts/build-layer.ts --arch aarch64 --release latest --output ./bun-lambda-layer.zip
Downloading... https://bun.sh/download/latest/linux/aarch64?avx2=true
Extracting...
Saving... ./bun-lambda-layer.zip
Saved
Publishing the layer is also very simple.
❯ bun run publish-layer -- \
--layer bun-lambda-layer \
--arch aarch64 \
--release latest \
--output ./bun-lambda-layer.zip \
--region eu-west-1
$ bun scripts/publish-layer.ts --layer bun-lambda-layer --arch aarch64 --release latest --output ./bun-lambda-layer.zip --region eu-west-1
Downloading... https://bun.sh/download/latest/linux/aarch64?avx2=true
Extracting...
Saving... ./bun-lambda-layer.zip
Saved
Publishing...
Published arn:aws:lambda:eu-west-1:651831719661:layer:bun-lambda-layer:1
Done
Checking the layer:
❯ aws lambda list-layers
| jq '.Layers[].LayerArn'
| xargs -I {} bash -c "aws lambda list-layer-versions --layer-name {} | jq '.LayerVersions[].LayerVersionArn'"
| egrep bun
"arn:aws:lambda:eu-west-1:651831719661:layer:bun-lambda-layer:1"
First we need a file with the assume role policy document.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
We can create the role called bun-lambda-role referencing the assume role policy.
❯ aws iam create-role --role-name bun-lambda-role --assume-role-policy-document file://role.policy.json
{
"Role": {
"Path": "/",
"RoleName": "bun-lambda-role",
"RoleId": "AROAZPRBSW3W7DL5YG6HZ",
"Arn": "arn:aws:iam::651831719661:role/bun-lambda-role",
"CreateDate": "2023-10-19T10:16:11+00:00",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
}
}
Creating the lambda function that handles the requests.
export default {
async handler(request: Request): Promise<Response> {
console.log(request.headers.get("x-amzn-function-arn"));
return new Response(JSON.stringify({"Hello": "from Bun runtime"}), {
status: 200,
headers: {
"Content-Type": "application/json",
},
});
},
};
Saving this as index.ts and then creating a zip file.
zip bun-function.zip index.ts
Finally, we can create the function:
❯ aws lambda create-function \
--function-name bun-lambda-function \
--runtime provided.al2 \
--architectures arm64 \
--layers arn:aws:lambda:eu-west-1:651831719661:layer:bun-layer:1 \
--zip-file fileb://bun-function.zip \
--handler index.handler \
--role arn:aws:iam::651831719661:role/bun-lambda-role
The response from the AWS API has all the details of the lambda function we just created:
{
"FunctionName": "bun-lambda-function",
"FunctionArn": "arn:aws:lambda:eu-west-1:651831719661:function:bun-lambda-function",
"Runtime": "provided.al2",
"Role": "arn:aws:iam::651831719661:role/bun-lambda-role",
"Handler": "index.handler",
"CodeSize": 365,
"Description": "",
"Timeout": 3,
"MemorySize": 128,
"LastModified": "2023-10-19T10:16:58.106+0000",
"CodeSha256": "yNUNBaGkJuist/ZoQkZtnToNNXIU/taYQHqZrwCu3Mk=",
"Version": "$LATEST",
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "c8edb420-aeeb-4568-8289-ea7b0b845d00",
"Layers": [
{
"Arn": "arn:aws:lambda:eu-west-1:651831719661:layer:bun-layer:1",
"CodeSize": 33260946
}
],
"State": "Pending",
"StateReason": "The function is being created.",
"StateReasonCode": "Creating",
"PackageType": "Zip",
"Architectures": [
"arm64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"RuntimeVersionConfig": {
"RuntimeVersionArn": "arn:aws:lambda:eu-west-1::runtime:dce29199fb5887a2c4fceaa2f34d395ba43a74a6895b381cb9383b1c7f3b5875"
}
}
To make the Lambda function accessible without any additional services we can enable function URL.
❯ aws lambda create-function-url-config \
--function-name bun-lambda-function \
--auth-type NONE
{
"FunctionUrl": "https://wbjmhbkwx5ikumwxkhka4lcgle0nvjgs.lambda-url.eu-west-1.on.aws/",
"FunctionArn": "arn:aws:lambda:eu-west-1:651831719661:function:bun-lambda-function",
"AuthType": "NONE",
"CreationTime": "2023-10-19T10:18:56.838382Z"
}
curl -vIX GET https://wbjmhbkwx5ikumwxkhka4lcgle0nvjgs.lambda-url.eu-west-1.on.aws/
{"Message":"Forbidden"}⏎
It seems something is missing. Digging through the possible reasons I found the following:
Your function URL auth type is NONE, but is missing permissions required
for public access. To allow unauthenticated requests, choose the Permissions
tab and create a resource-based policy that grants lambda:invokeFunctionUrl
permissions to all principals (*). Alternatively, you can update your function
URL auth type to AWS_IAM to use IAM authentication.
Let’s create the missing permission:
The JSON version of the policy:
{
"Version": "2012-10-17",
"Id": "default",
"Statement": [
{
"Sid": "FunctionURLAllowPublicAccess",
"Effect": "Allow",
"Principal": "*",
"Action": "lambda:InvokeFunctionUrl",
"Resource": "arn:aws:lambda:eu-west-1:651831719661:function:bun-lambda-function",
"Condition": {
"StringEquals": {
"lambda:FunctionUrlAuthType": "NONE"
}
}
}
]
}
Finally, we can invoke the function:
❯ curl -iX GET https://wbjmhbkwx5ikumwxkhka4lcgle0nvjgs.lambda-url.eu-west-1.on.aws/
HTTP/1.1 200 OK
Date: Thu, 19 Oct 2023 12:01:29 GMT
Content-Type: application/json
Content-Length: 28
Connection: keep-alive
x-amzn-RequestId: cd204d16-01c9-4468-a02e-9f6d7d3ee2e1
X-Amzn-Trace-Id: root=1-65311a98-3ead96f16ebd86bc6366d05b;sampled=0;lineage=fc7d7573:0
{"Hello":"from Bun runtime"}
Latency looks reasonable:
That’s all folks!