armature-lambda 0.3.0

AWS Lambda runtime adapter for Armature applications
Documentation

armature-lambda

AWS Lambda runtime adapter for the Armature framework.

Features

  • Lambda Runtime - Run Armature apps on Lambda
  • API Gateway - HTTP event handling
  • ALB - Application Load Balancer support
  • Cold Start Optimization - Minimal startup time
  • Layers - Shared dependencies

Installation

[dependencies]
armature-lambda = "0.1"

Quick Start

The runtime wraps any type that implements RequestHandler. The simplest handler is a closure taking a LambdaRequest and returning a LambdaResponse:

use armature_lambda::{LambdaRequest, LambdaResponse, LambdaRuntime};

#[tokio::main]
async fn main() -> Result<(), lambda_runtime::Error> {
    armature_lambda::init_tracing();

    let handler = |req: LambdaRequest| async move {
        LambdaResponse::ok(format!("Hello from {}!", req.path))
    };

    LambdaRuntime::new(handler).run().await
}

Configuration

Use LambdaConfig (via with_config) to control logging and strip an API Gateway stage prefix such as /prod:

use armature_lambda::{LambdaConfig, LambdaRuntime};

let config = LambdaConfig::default()
    .log_requests(true)
    .log_responses(false)
    .base_path("/prod");

// `app` is any type implementing `RequestHandler`.
let runtime = LambdaRuntime::new(app).with_config(config);
runtime.run().await?;

Adapting an existing application type

This crate does not convert between armature_core's HttpRequest / HttpResponse and the Lambda event types, and there is no blanket RequestHandler implementation for an Armature Application. What it offers is impl_lambda_handler!, which removes the trait boilerplate around a handle_request method you write:

use armature_lambda::{impl_lambda_handler, LambdaRequest, LambdaRuntime};

struct MyApp { /* your Armature Application, router, etc. */ }

struct MyResponse {
    status: u16,
    body: Vec<u8>,
    headers: Vec<(String, String)>,
}

impl MyApp {
    // You write this: translate `LambdaRequest` into whatever your application
    // consumes, and its result back into the shape below.
    async fn handle_request(
        &self,
        request: LambdaRequest,
    ) -> Result<MyResponse, std::io::Error> {
        // ...
    }
}

impl_lambda_handler!(MyApp);

#[tokio::main]
async fn main() -> Result<(), lambda_runtime::Error> {
    armature_lambda::init_tracing();
    LambdaRuntime::new(MyApp { /* .. */ }).run().await
}

The macro forwards the whole LambdaRequest — method, path, headers, query string, path parameters, stage variables, and authorizer claims — so nothing is lost on the way in, and maps status/body/headers back out (any Display error becomes a 500).

The runtime auto-detects API Gateway (REST v1 / HTTP v2), ALB, and Lambda Function URL events — no separate constructor is required.

Headers

Request and response headers are Vec<(String, String)>, not maps, so repeated names survive in both directions. In particular a handler can emit more than one Set-Cookie:

LambdaResponse::ok("done")
    .header("set-cookie", "session=abc; HttpOnly")
    .header("set-cookie", "csrf=xyz");

header(..) appends; use set_header(..) to replace existing lines with the same name. Read them back with header_value(..) (first match) or header_values(..) (all, in order).

Build for Lambda

# Install cargo-lambda
cargo install cargo-lambda

# Build
cargo lambda build --release

# Deploy
cargo lambda deploy my-function

License

MIT OR Apache-2.0