Luciano Mammino PRO
Cloud developer, entrepreneur, fighter, butterfly maker! #nodejs #javascript - Author of https://www.nodejsdesignpatterns.com , Founder of https://fullstackbulletin.com
Luciano Mammino (@loige)
2024-08-20
Luciano Mammino (@loige)
2024-08-20
Luciano Mammino (@loige)
2024-08-20
This guy has the best title ideas!
𝕏 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
𝕏 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
Extra 10% discount with the code "BELIEVE" 🤑🤑
Early-access available at
50% discount! 🤑
Thanks to @gbinside, @conzy_m, @eoins, and @micktwomey for kindly reviewing this talk!
THANKS!
Grab these slides!
𝕏 loige
By Luciano Mammino
Join AWS serverless hero Luciano Mammino as he gives you the practical details to getting started with Rust in Lambda. Bite-sized functions are the perfect way to learn about this blazing fast programming language that is taking the tech industry by storm. Get ready as Luciano gets rusty and shares the how, what, where, and why you should try it out yourself.
Cloud developer, entrepreneur, fighter, butterfly maker! #nodejs #javascript - Author of https://www.nodejsdesignpatterns.com , Founder of https://fullstackbulletin.com