/lambda-web

Run Rust web frameworks on AWS Lambda

Primary LanguageRustMIT LicenseMIT

lambda-web

Run Rust web server frameworks on AWS Lambda. Currently, it supports Actix web, axum, Rocket, warp.

crates.io API docs

Features

Supported web frameworks

Supported AWS infrastructure

Not supported

Example

Actix Web

Cargo.toml

[[bin]]
name = "bootstrap"
path = "src/main.rs"

[dependencies]
lambda-web = { version = "0.1.8", features=["actix4"] }

main.rs

use lambda_web::actix_web::{self, get, App, HttpServer, Responder};
use lambda_web::{is_running_on_lambda, run_actix_on_lambda, LambdaError};

#[get("/")]
async fn hello() -> impl Responder {
    format!("Hello")
}

#[actix_web::main]
async fn main() -> Result<(),LambdaError> {
    let factory = move || {
        App::new().service(hello)
    };

    if is_running_on_lambda() {
        // Run on AWS Lambda
        run_actix_on_lambda(factory).await?;
    } else {
        // Local server
        HttpServer::new(factory)
            .bind("127.0.0.1:8080")?
            .run()
            .await?;
    }
    Ok(())
}

axum

Cargo.toml

[[bin]]
name = "bootstrap"
path = "src/main.rs"

[dependencies]
lambda-web = { version = "0.1.8", features=["hyper"] }
axum = "0.3"
tokio = { version = "1" }

main.rs

use axum::{routing::get, Router};
use lambda_web::{is_running_on_lambda, run_hyper_on_lambda, LambdaError};
use std::net::SocketAddr;

// basic handler that responds with a static string
async fn root() -> &'static str {
    "Hello, World!"
}

#[tokio::main]
async fn main() -> Result<(), LambdaError> {
    // build our application with a route
    let app = Router::new().route("/", get(root));

    if is_running_on_lambda() {
        // Run app on AWS Lambda
        run_hyper_on_lambda(app).await?;
    } else {
        // Run app on local server
        let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
        axum::Server::bind(&addr).serve(app.into_make_service()).await?;
    }
    Ok(())
}

Rocket

Cargo.toml

[[bin]]
name = "bootstrap"
path = "src/main.rs"

[dependencies]
lambda-web = { version = "0.1.8", features=["rocket05"] }
rocket = "0.5.0-rc.1"

main.rs

use rocket::{self, get, routes};
use lambda_web::{is_running_on_lambda, launch_rocket_on_lambda, LambdaError};

#[get("/hello/<name>/<age>")]
fn hello(name: &str, age: u8) -> String {
    format!("Hello, {} year old named {}!", age, name)
}

#[rocket::main]
async fn main() -> Result<(), LambdaError> {
    let rocket = rocket::build().mount("/", routes![hello]);
    if is_running_on_lambda() {
        // Launch on AWS Lambda
        launch_rocket_on_lambda(rocket).await?;
    } else {
        // Launch local server
        rocket.launch().await?;
    }
    Ok(())
}

warp

Cargo.toml

[[bin]]
name = "bootstrap"
path = "src/main.rs"

[dependencies]
lambda-web = { version = "0.1.8", features=["hyper"] }
warp = "0.3"
tokio = { version = "1" }

main.rs

use lambda_web::{is_running_on_lambda, run_hyper_on_lambda, LambdaError};
use warp::Filter;

#[tokio::main]
async fn main() -> Result<(), LambdaError> {
    // GET /hello/warp => 200 OK with body "Hello, warp!"
    let hello = warp::path!("hello" / String).map(|name| format!("Hello, {}", name));

    if is_running_on_lambda() {
        // Run on AWS Lambda
        run_hyper_on_lambda(warp::service(hello)).await?;
    } else {
        // Run local server
        warp::serve(hello).run(([127, 0, 0, 1], 8080)).await;
    }
    Ok(())
}

Create deploy ZIP file

As of writing (Nov, 2021), we have two options to run Rust on AWS Lambda: Amazon Linux 2 custom runtime or Docker container image.

I recommend ZIP deploy to Amazon Linux 2 custom runtime (provided.al2) because it's faster cold start time than container image.

To build Amazon Linux 2 compatible binary, see Deploy.md for more details.

Setup AWS Lambda & API gateway

Lambda

  • Create lambda function with provided.al2 custom runtime. Choose "Provide your own bootstrap on Amazon Linux 2" .
  • Upload ZIP file described above.
  • IAM role, memory settings, etc. are as your demands.
    As sample code above consumes only 30MB of memory, many simple Rust app can fit in 128MB setting.

API Gateway (HTTP)

  • Create HTTP API
  • Create single route "$default" and attach Lambda integration. Make sure, payload format version is "2.0"

API Gateway (REST)

  • Create REST API
  • Create two resources:
    • ANY method on route / and attach Lambda proxy integration.
    • ANY method on route /{proxy+} and attach Lambda proxy integration.
  • In settings tab, add */* binary media type.
  • Then, deploy API to stage.