This package makes it easy to run AWS Lambda Functions written in Rust. This workspace includes multiple crates:
lambda-runtime
is a library that provides a Lambda runtime for applications 8000 written in Rust.lambda-http
is a library that makes it easy to write API Gateway proxy event focused Lambda functions in Rust.lambda-extension
is a library that makes it easy to write Lambda Runtime Extensions in Rust.lambda-events
is a library with strongly-typed Lambda event structs in Rust.lambda-runtime-api-client
is a shared library between the lambda runtime and lambda extension libraries that includes a common API client to talk with the AWS Lambda Runtime API.
The Rust runtime client is an experimental package. It is subject to change and intended only for evaluation purposes.
The easiest way to start writing Lambda functions with Rust is by using Cargo Lambda, a related project. Cargo Lambda is a Cargo plugin, or subcommand, that provides several commands to help you in your journey with Rust on AWS Lambda.
The preferred way to install Cargo Lambda is by using a package manager.
1- Use Homebrew on MacOS:
brew tap cargo-lambda/cargo-lambda
brew install cargo-lambda
2- Use Scoop on Windows:
scoop bucket add cargo-lambda https://github.com/cargo-lambda/scoop-cargo-lambda
scoop install cargo-lambda/cargo-lambda
Or PiP on any system with Python 3 installed:
pip3 install cargo-lambda
Alternative, install the pip package as an executable using uv
uv tool install cargo-lambda
See other installation options in the Cargo Lambda documentation.
To create your first function, run Cargo Lambda with the subcommand new
. This command will generate a Rust package with the initial source code for your function:
cargo lambda new YOUR_FUNCTION_NAME
If you'd like to manually create your first function, the code below shows you a simple function that receives an event with a firstName
field and returns a message to the caller.
use lambda_runtime::{service_fn, LambdaEvent, Error};
use serde_json::{json, Value};
#[tokio::main]
async fn main() -> Result<(), Error> {
let func = service_fn(func);
lambda_runtime::run(func).await?;
Ok(())
}
async fn func(event: LambdaEvent<Value>) -> Result<Value, Error> {
let (event, _context) = event.into_parts();
let first_name = event["firstName"].as_str().unwrap_or("world");
Ok(json!({ "message": format!("Hello, {}!", first_name) }))
}
when a function invocation fails, AWS Lambda expects you to return an object that can be serialized into JSON structure with the error information. This structure is represented in the following example:
{
"error_type": "the type of error raised",
"error_message": "a string description of the error"
}
The Rust Runtime for Lambda uses a struct called Diagnostic
to represent function errors internally. The runtime implements the conversion of several general error types, like std::error::Error
, into Diagnostic
. For these general implementations, the error_type
is the name of the value type returned by your function. For example, if your function returns lambda_runtime::Error
, the error_type
will be something like alloc::boxed::Box<dyn core::error::Error + core::marker::Send + core::marker::Sync>
, which is not very descriptive.
To get more descriptive error_type
fields, you can implement From
for your error type. That gives you full control on what the error_type
is:
use lambda_runtime::{Diagnostic, Error, LambdaEvent};
#[derive(Debug)]
struct ErrorResponse(&'static str);
impl From<ErrorResponse> for Diagnostic {
fn from(error: ErrorResponse) -> Diagnostic {
Diagnostic {
error_type: "MyErrorType".into(),
error_message: error.0.to_string(),
}
}
}
async fn handler(_event: LambdaEvent<()>) -> Result<(), ErrorResponse> {
Err(ErrorResponse("this is an error response"))
}
We recommend you to use the thiserror crate to declare your errors. You can see an example on how to integrate thiserror
with the Runtime's diagnostics in our example repository
Popular error crates like Anyhow, Eyre, and Miette provide their own error types that encapsulate other errors. There is no direct transformation of those errors into Diagnostic
, but we provide feature flags for each one of those crates to help you integrate them with your Lambda functions.
If you enable the features anyhow
, eyre
, or miette
in the lambda_runtime
dependency of your package. The error types provided by those crates can have blanket transformations into Diagnostic
. These features expose an From<T> for Diagnostic
implementation that transforms those error types into a Diagnostic
. This is an example that transforms an anyhow::Error
into a Diagnostic
:
use lambda_runtime::{Diagnostic, LambdaEvent};
async fn handler(_event: LambdaEvent<Request>) -> Result<(), Diagnostic> {
Err(anyhow::anyhow!("this is an error").into())
}
You can see more examples on how to use these error crates in our example repository.
lambda_runtime
offers a helper to simplify configuring graceful shutdown signal handling, spawn_graceful_shutdown_handler()
. This requires the graceful-shutdown
feature flag and only supports Unix systems.
You can use it by passing a FnOnce
closure that returns an async block. That async block will be executed
when the function receives a SIGTERM
or SIGKILL
.
Note that this helper is opinionated in a number of ways. Most notably:
- It spawns a task to drive your signal handlers
- It registers a 'no-op' extension in order to enable graceful shutdown signals
- It panics on unrecoverable errors
If you prefer to fine-tune the behavior, refer to the implementation of spawn_graceful_shutdown_handler()
as a starting point for your own.
For more information on graceful shutdown handling in AWS Lambda, see: aws-samples/graceful-shutdown-with-aws-lambda.
Complete example (cleaning up a non-blocking tracing writer):
use lambda_runtime::{service_fn, LambdaEvent, Error};
use serde_json::{json, Value};
#[tokio::main]
async fn main() -> Result<(), Error> {
let func = service_fn(func);
let (writer, log_guard) = tracing_appender::non_blocking(std::io::stdout());
lambda_runtime::tracing::init_default_subscriber_with_writer(writer);
let shutdown_hook = || async move {
std::mem::drop(log_guard);
};
lambda_runtime::spawn_graceful_shutdown_handler(shutdown_hook).await;
lambda_runtime::run(func).await?;
Ok(())
}
async fn func(event: LambdaEvent<Value>) -> Result<Value, Error> {
let (event, _context) = event.into_parts();
let first_name = event["firstName"].as_str().unwrap_or("world");
Ok(json!({ "message": format!("Hello, {}!", first_name) }))
}
If you already have Cargo Lambda installed in your machine, run the next command to build your function:
cargo lambda build --release
There are other ways of building your function: manually with the AWS CLI, with AWS SAM, and with the Serverless framework.
< 8000 div class="markdown-heading" dir="auto">