On a Sunday morning, as you walk towards the bakery, the aroma of freshly baked buns fills the air. You can't resist taking a bite of the hot bun fresh from the oven as soon as you get it.
Now, it’s most likely that you burn yourself or you are smart and let it cool down a bit.
Bun is so cool that I also got hyped. However, I questioned myself:
- How can I use it in my current or future projects?
- How can I deploy it?
- Do I have to rewrite a lot or even everything?
In this blog post, we will discover the usage of Bun by creating an AWS CDK template using Bun tooling.
What is Bun?
The NodeJS was established long ago but had no unified tooling and an ever-changing ecosystem. Bun wants to become the all-in-one JavaScript toolkit, including runtime, package manager, test runner, and bundler. It is built from scratch with three primary design goals: fast startup, fast running performance, and cohesive DX.
What does it mean “Runtime”?
It means how your computer interprets your script. Each programming language has its Syntax and grammar; you can run a program using their language1. For example, if you write a Python script, you run it with python myscript.py.
The machine uses Python as runtime to interpret it. The file extension allows humans to understand that this is a Python file or editors to do color highlighting, but it’s unnecessary for the machine.
The same applies to NodeJS and Bun. You can execute the script by running node myscript.js
and bun myscript.js
. Notice that Bun can run Javascript.
More interesting is what Syntax Bun supports. - A bun of (pun intended ✊) including Typescript, JSX, and JSON2.
What if we want to deploy a function to any Cloud provider such as Cloudflare or AWS? None of them support these for now (28. September 2023).
Remember, we need to run bun myscript.ts.
Luckily, on AWS Lambda, we could create a custom runtime.
That means we don’t need to transpile the Typescript code to ESM or CJS. Currently, only Deno Deploy can run your Typescript function out of the box. However, in order to keep the code small, we still need some sort of bundling. Luckily, Bun is also a bundler 😉
What is a Bundler?
A bundler is a tool that puts your code into a single file. When you develop a function, you may split your code into multiple files and use third-party packages. So, instead of shipping the whole node_modules
- folder or all the libraries, a bundler only uses the correct code.
We do this to optimize JavaScript files by reducing their size and number, which can significantly improve website performance, resulting in better user experience and increased engagement.
Furthermore, Node-Server cannot understand Typescript, so we use a bundler to transpile and bundle.
The famous bundler is Webpack, but esbuild is the fastest one… Well, until Bun 🍔
Bunnyfied AWS CDK project 🐰
As a cloud engineer, I am working a lot with AWS CDK, a tool for infrastructure on AWS. Usually, I develop my AWS Resources in SST (here are my thoughts on SST and not in Projen (here are my thoughts on Projen.
AWS CDK
AWS CDK is an open-source software development framework developed by Amazon Web Services (AWS) that allows developers to define and provision cloud infrastructure resources using familiar programming languages.
Their CLI bootstraps an npm project. I used that to Bunyfied it.
bunx cdk init app --language typescript
For fun, I decided to test which package manager is the fastest for creating an AWS CDK project. It appears that bunx
is faster than npx
and pnpx
. However, this was not a proper benchmarking test.
On this attempt bunx
runs the fastest but is not really impressive.
bun test
and bun install
When I first did it, I removed jest
and package-lock.json
because I wanted to try out the native Bun Test, and I used Bun’s package manager to install packages.
Testing
Bun comes with its own testing suite, and it gives you nice hints in order to make this test pass 😇
And I have to say the --watch
mode is really fast. vitest
is also fast and reruns only failed tests, which makes it, in the end, faster (here when using Monorepo).
However, Mocking didn't work as expected. I attempted to spy on a function to manipulate a result but was unsuccessful. Their mocks provided were also unusable when trying to mock out the functions of packages. The documentation was not helpful either. As a result, I had to abandon bun test
and used vitest
instead.
Package Manager
Bun is clearly the fastest, and this time, you can really feel it 🤩
When I installed Lambda Powertools, Bun became significantly faster. It turns out that I had already installed some packages, but still, Bun was faster than pnpm.
Workspaces
I created a Monorepo because Bun supports Workspaces).
Deploying a Bun Runtime
Bun has its own package for creating a Lambda Layer.
I created a BunFunLayerStack
to deploy a Lambda Layer. That layer is customizable and could be consumed by another account, or you could even make it public.
BunFun Construct
Then, I created a Construct BunFun
which creates a Fun
ction using the Lambda Layer.
It tries to read Arn, resulting from the BunFunLayerStack
but can be overwritten when passing props.bunLayer
in case you bring your own Layer.
By default, the respective target will be used. If it's a Typescript or Javascript file, then node
it will be the target. But then you could use the NodeJsFunction
- Construct of CDK. However, when you need the Bun there is a bunConfig
- property that also opens up all the Bun configurations from their bundler API.
I configured that it is optional, but when you use it, make sure you add a target. Sometimes I love Typescript 🤩
Generally, I put three examples in the BunFunStack
.
Run it
I put a couple of Lambdas in the packages/functions
to get started and I realized that the Lambdas using that runtime must always return a new Response
. Otherwise, the Lambda will not execute successfully. But that doesn't mean you cannot use it for handling SQS.
BunFunStack
and BunFunLayerStack
can be deployed separately. In this repo, I put them into an BunOvenStack
.
Simply run bun deploy:all
to deploy the BunOvenStack
and run bun deploy:only
to deploy only the BunFunStack
. The bun deploy:only
is also parameterized and could run another stack e.g. STACK=BunFunLayerStack bun deploy:only
.
Conclusion
In this blog post, I used Bun's tooling to create an AWS CDK project. It includes a Layer for deploying a Bun Runtime and a Construct for creating a BunFunction.
Why should we care? Well, Bun would eliminate the fact of either using CommonJS or EcmaScript Modules (ESM) because most of the developers (me included) simply don't care. With the Bun runtime, you would run your Typescript files without transpiling them. Furthermore, Bun is fast. This blog does not cover benchmarking the Lambdas with a Bun runtime as there are already Benchmarks for Bun’s performance3.
Unfortunately, bun test
was not usable, and I use vitest
instead. Moreover, the Lambda Runtime requires you always return new Response
when using the bun
target. This could be confusing when using Lambda for a different purpose then using an API Gateway.
With my Bunnyfied AWS CDK template, you could already start deploying Lambda functions using Bun Runtime, so give it a shoot 😉
To try it out, use the following command
bun create github.com/jolo-dev/bunnyfied-aws-cdk
P.S.: It's faster than the npx cdk init app
.
P.S.S: Feedback and Contributions are welcome!
Top comments (0)