🏃 🌱 🤑 🐑 🦀
Luciano Mammino (@loige)
2024-11-26
𝕏 loige
𝕏 loige
𝕏 loige
𝕏 loige
Zero-cost abstractions
Strongly typed with a really good type-system
Takes inspiration from Haskell, C++, OCaml, JavaScript, Ruby
Great (built-in) package manager (Cargo)
Great ecosystem of libraries
Pattern matching, no null, Option & Result types
𝕏 loige
𝕏 loige
𝕏 loige
use std::env;
fn main() {
let region = env::var("AWS_REGION");
}
Result<String, VarError>
𝕏 loige
😀 Happy path
🥺 Sad path
use std::env;
fn main() {
let region = env::var("AWS_REGION");
match region {
Ok(region) => println!("Selected region: {}", region),
Err(_) => println!("Error: AWS_REGION not set"),
}
}
𝕏 loige
😀 Happy path
🥺 Sad path
use std::env;
fn main() {
let region = env::var("AWS_REGION")
.expect("AWS_REGION environment variable not set");
}
String
𝕏 loige
If you cannot get the value, panic!
use std::env;
fn main() {
let region = env::var("AWS_REGION")
.unwrap_or_else(|_| "eu-west-1".to_string());
}
Rust makes it very hard for you to ignore possible errors or the absence of values.
𝕏 loige
if you cannot get the value, use a default value!
𝕏 loige
👋 I'm Luciano (🇮🇹🍕🍝🤌)
👨💻 Senior Architect @ fourTheorem
📔 Co-Author of Node.js Design Patterns 👉
Let's connect!
✉️ Reach out to us at hello@fourTheorem.com
😇 We are always looking for talent: fth.link/careers
We can help with:
Cloud Migrations
Training & Cloud enablement
Building high-performance serverless applications
Cutting cloud costs
𝕏 loige
𝕏 loige
𝕏 loige
𝕏 loige
𝕏 loige
𝕏 loige
RUST?!
𝕏 loige
RUST?!
𝕏 loige
EASY PEASY... we just need a custom runtime! 🤗
𝕏 loige
𝕏 loige
Runtime
Handler (logic)
𝕏 loige
Runtime
Handler (logic)
Poll for events
𝕏 loige
Runtime
Handler (logic)
Poll for events
event (JSON)
𝕏 loige
Runtime
Handler (logic)
Poll for events
event (JSON)
execute
𝕏 loige
Runtime
Handler (logic)
Poll for events
event (JSON)
execute
response or
error
𝕏 loige
Runtime
Handler (logic)
Poll for events
event (JSON)
execute
response or
error
response (JSON)
or error
𝕏 loige
𝕏 loige
𝕏 loige
𝕏 loige
Load the handler code
Infinite loop
𝕏 loige
𝕏 loige
𝕏 loige
𝕏 loige
𝕏 loige
𝕏 loige
𝕏 loige
use aws_lambda_events::event::s3::S3Event;
use lambda_runtime::{run, service_fn, Error, LambdaEvent};
async fn function_handler(event: LambdaEvent<S3Event>) -> Result<(), Error> {
for record in event.payload.records {
tracing::info!(
"[{}] Bucket={} Key={}",
record.event_name.unwrap_or_default(),
record.s3.bucket.name.unwrap_or_default(),
record.s3.object.key.unwrap_or_default()
);
}
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Error> {
tracing::init_default_subscriber();
run(service_fn(function_handler)).await
}
𝕏 loige
𝕏 loige
𝕏 loige
async fn function_handler(event: LambdaEvent<S3Event>)
-> Result<(), Error> {
// ...
Ok(())
}
Request
Response
What if we want to use different types? 🤨
𝕏 loige
use type definitions in the aws_lambda_events crate
𝕏 loige
𝕏 loige
Jobs
𝕏 loige
# Cargo.toml
[dependencies]
aws_lambda_events = {
version = "0.15.0",
default-features = false,
features = [
"sqs",
]
}
𝕏 loige
use aws_lambda_events::event::sqs::{BatchItemFailure, SqsBatchResponse, SqsEvent};
// ...
async fn function_handler(event: LambdaEvent<SqsEvent>)
-> Result<SqsBatchResponse, Error> {
let mut failed_jobs = Vec::with_capacity(event.payload.records.len());
for record in event.payload.records {
// process the job
// ...
// if the job failed, add it to the failed_jobs list
failed_jobs.push(BatchItemFailure {
item_identifier: record.message_id.unwrap_or_default(),
});
}
Ok(SqsBatchResponse {
batch_item_failures: failed_jobs,
})
}
// ...
𝕏 loige
Create custom request and response types
𝕏 loige
𝕏 loige
# Cargo.toml
[dependencies]
serde = "1.0.200"
serde_json = "1.0.116"
𝕏 loige
// ...
#[derive(serde::Deserialize)]
struct Request {
url: String,
}
#[derive(serde::Serialize)]
struct Response {
issue_number: u32,
}
async fn function_handler(event: LambdaEvent<Request>)
-> Result<Response, Error> {
println!("I am going to scrape {}", event.payload.url);
// TODO: actual scraping logic here
Ok(Response { issue_number: 333 })
}
// ...
𝕏 loige
𝕏 loige
Use arbitrary JSON values!
𝕏 loige
// ...
async fn function_handler(
event: LambdaEvent<serde_json::Value>,
) -> Result<serde_json::Value, Error> {
let url = event
.payload
.as_object()
.unwrap()
.get("url")
.unwrap()
.as_str()
.unwrap(); // 🤮
println!("I am going to scrape {}", url);
// TODO: actual scraping logic here
Ok(serde_json::json!({ "issue_number": 333 }))
}
// ...
𝕏 loige
𝕏 loige
𝕏 loige
use lambda_http::{run, service_fn, Body, Error, Request, RequestExt, Response};
async fn function_handler(event: Request) -> Result<Response<Body>, Error> {
// Extract some useful information from the request
let who = event
.query_string_parameters_ref()
.and_then(|params| params.first("name"))
.unwrap_or("world");
let message = format!("Hello {who}, this is an AWS Lambda HTTP request");
// Return something that implements IntoResponse.
// It will be serialized to the right response event
// automatically by the runtime
let resp = Response::builder()
.status(200)
.header("content-type", "text/html")
.body(message.into())
.map_err(Box::new)?;
Ok(resp)
}
𝕏 loige
use lambda_http::{run, service_fn, Body, Error, Request, RequestExt, Response};
async fn function_handler(event: Request) -> Result<Response<Body>, Error> {
// Extract some useful information from the request
let who = event
.query_string_parameters_ref()
.and_then(|params| params.first("name"))
.unwrap_or("world");
let message = format!("Hello {who}, this is an AWS Lambda HTTP request");
// Return something that implements IntoResponse.
// It will be serialized to the right response event
// automatically by the runtime
let resp = Response::builder()
.status(200)
.header("content-type", "text/html")
.body(message.into())
.map_err(Box::new)?;
Ok(resp)
}
These are just abstractions! 🧐
Lambda is still using JSON behind the scenes.
For HTTP you generally use the Lambda-Proxy integration.
𝕏 loige
cargo lambda build --release && cargo lambda deploy
𝕏 loige
𝕏 loige
NO TRIGGER CONFIGURED! 🙄
WUT!? 😱
cargo lambda deploy --enable-function-url
𝕏 loige
SAM Works with Cargo Lambda (beta feature):
Note: Cargo Lambda also works with CDK
(github.com/cargo-lambda/cargo-lambda-cdk)
𝕏 loige
# template.yaml
AWSTemplateFormatVersion: "2010-09-09"
Transform:
- AWS::Serverless-2016-10-31
Resources:
ExampleHttpLambda:
Type: AWS::Serverless::Function
Metadata:
BuildMethod: rust-cargolambda
Properties:
CodeUri: .
Handler: bootstrap
Runtime: provided.al2023
Architectures:
- arm64
Events:
HttpPost:
Type: Api
Properties:
Path: /
Method: get
# samconfig.toml
version = 0.1
[default]
[default.global]
[default.global.parameters]
stack_name = "rust-http-lambda"
[default.build.parameters]
beta_features = true
[default.sync.parameters]
beta_features = true
Tells SAM to build using Cargo Lambda
Selects a "custom runtime"
Defines an HTTP trigger
(API Gateway)
Enables SAM beta features
𝕏 loige
sam build sam local start-api sam deploy
𝕏 loige
𝕏 loige
𝕏 loige
𝕏 loige
𝕏 loige
𝕏 loige
𝕏 loige
𝕏 loige
50% discount with the code "PORTOTECH" 🤑🤑
Thanks to @gbinside, @conzy_m, @eoins, and @micktwomey for kindly reviewing this talk!
THANKS!
Grab these slides!
𝕏 loige