Hey there, Crystal community! Today I’m here to share something I’ve been working on lately.
As you probably already know, Lambda is the Function as a Service offering from AWS. It supports many runtimes out-of-the-box, such as Node, Python and Ruby, but it also allows the use of custom runtimes, which means that unsupported languages can also be used to make Lambdas.
There were already successful attempts to create Lambda runtimes for Crystal, such as Crambda (if you are Crambda’s creator, thank you so much for your work! I used it in production for months and your clean and straightforward implementation gave me confidence that this was a problem that I could realistically tackle), but they imposed some challenges that increased the friction during deployment, such as the lack of a building tool. Hence, I created Crowbar, a complete solution to run Crystal code in AWS Lambdas.
Crowbar can be viewed as a package consisting of three parts: the runtime itself, which is the direct alternative to existing solutions such as Crambda; the web adapters, which allow web servers to be deployed as lambdas (as far as I’m aware of, there is no other Crystal shard that implements this feature); and a CLI tool to easily build and bundle projects in an efficient and compatible way.
Here are some of the features/improvements of each Crowbar component:
-
Runtime: The lambda runtime is the piece of code responsible for the communication between the user code and the AWS lambda environment.
- Type safety: input and output types are handled through generics in a convenient and performant way; no need to deal with
JSON::Any
unless you explicitly want to; - Error handling: Crowbar implements the error handling specification for lambda runtimes, allowing it to run even after exceptions;
- Streaming Response support: somewhat recently, AWS announced Streaming Responses, which is available for some languages, now Crystal is one of them.
- Type safety: input and output types are handled through generics in a convenient and performant way; no need to deal with
-
Web Adapter: Crowbar includes an abstraction layer that allows existing web applications built with
HTTP::Server
to be deployed as lambdas with just a few lines of code.- Supports lambdas deployed behind API Gateway, Application Load Balancer and Lambda URLs;
- Handcrafted serialization and deserialization to ensure minimal overhead.
-
CLI Tool: to run on lambda, projects must be built targeting the lambda environment and ship any unavailable libraries together with user code. Crowbar abstracts this process away with a CLI that leverages Docker to ensure compatibility, allowing developers to focus on code instead of the build process.
- Easy to use: the entire build process can be done with a single command;
- Dependency detection: Crowbar keeps track of readily available libraries on lambda and only includes dependencies when they are needed;
- Cross compilation: the build can target both x86_64 and arm64 environments;
- Extendability: if for whatever reason you need full control over your build process, instead of directly building the project, the CLI can be used to bootstrap build scripts that can be further audited and customized.
This is an example of a simple lambda built with Crowbar:
require "json"
require "crowbar"
class Event
include JSON::Serializable
getter operand : Int32
end
Crowbar.handle_events of_type: Event do |event, context|
{result: event.operand + 1}
end
And this is a simple example of a Kemal app running using one of Crowbar’s adapters:
require "crowbar"
require "crowbar/http"
require "kemal"
get "/" do
"Hello World!"
end
# Initialize server instance
Kemal.run { Kemal.stop }
# Magic line!
Crowbar.handle_events with: Kemal.config.server.not_nil!, using: Crowbar::LambdaURLAdapter
Shout out to ysbaddaden and straight-shoota, who gave me some valuable insights for the HTTP adapter layer; thanks, guys!
And that’s it! With this, I hope people will have a much easier time deploying lambdas. Let’s push Crystal into even more places!