{"id":19725035,"url":"https://github.com/seanpianka/rust-routerify-aws-lambda-quickstart","last_synced_at":"2026-05-02T05:03:54.587Z","repository":{"id":138019906,"uuid":"270062573","full_name":"seanpianka/rust-routerify-aws-lambda-quickstart","owner":"seanpianka","description":"A quickstart for setting up an HTTP API in AWS Lambda using Rust v1.43 and Routerify v1.1.4","archived":false,"fork":false,"pushed_at":"2020-10-17T23:28:24.000Z","size":46,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-12-15T15:00:12.591Z","etag":null,"topics":["api","aws","aws-lambda","http-api","lambda","rest-api","routerify","rust"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/seanpianka.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-06-06T18:06:18.000Z","updated_at":"2025-02-09T02:22:08.000Z","dependencies_parsed_at":null,"dependency_job_id":"d65d3752-9427-46e3-bd55-bdc8875e41c7","html_url":"https://github.com/seanpianka/rust-routerify-aws-lambda-quickstart","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/seanpianka/rust-routerify-aws-lambda-quickstart","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seanpianka%2Frust-routerify-aws-lambda-quickstart","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seanpianka%2Frust-routerify-aws-lambda-quickstart/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seanpianka%2Frust-routerify-aws-lambda-quickstart/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seanpianka%2Frust-routerify-aws-lambda-quickstart/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/seanpianka","download_url":"https://codeload.github.com/seanpianka/rust-routerify-aws-lambda-quickstart/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seanpianka%2Frust-routerify-aws-lambda-quickstart/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32523428,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-02T01:12:54.858Z","status":"online","status_checked_at":"2026-05-02T02:00:05.923Z","response_time":132,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","aws","aws-lambda","http-api","lambda","rest-api","routerify","rust"],"created_at":"2024-11-11T23:27:58.299Z","updated_at":"2026-05-02T05:03:54.570Z","avatar_url":"https://github.com/seanpianka.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# rust-routerify-aws-lambda-quickstart\nA quickstart for setting up an HTTP API in AWS Lambda / API Gateway using Rust v1.43 and Routerify v1.1.4.\n\n## What?\n\nBelow is a description of the steps to route and serve HTTP requests from AWS API Gateway and AWS Lambda to a Rust application (using a Rust HTTP routing library).\n\n### Why?\n\nFor most use cases, serving a web API with a traditional 24x7 hosting server _works_. However, there are use cases without specialized hardware requirements, which create an opportunity to reduce costs through the use of host-agnostic computing platforms (i.e. [Function-as-a-Service](https://en.wikipedia.org/wiki/Function_as_a_service) platforms like AWS Lambda).\n\n### Why Rust?\n\nRust is an expressive, fast, and reliable language to use for building any applications, once a point in the learning curve has been reached... It has a number of key benefits over other languages for greenfield projects:\n\n* Rust's type system allows for concise and expressive modeling of business domains and their invariants,\n* C-level speeds can be achieved with a memory-safe implementation for all of your project's technical details, \n* and a fast start-up time along with low runtime memory overhead allows us to take the most advantage of AWS Lambda's pricing at scale.\n\n## The Steps Described\n\nCreate the following `Cargo.toml`:\n\n```toml\n[package]\nauthors = [\"Bob Smith \u003cbob@example.com\u003e\"]\nedition = \"2018\"\nname = \"\u003cyour-crate-name\u003e\"\nversion = \"0.0.1\"\n\n[dependencies]\nhyper = \"0.13.6\"\n# This version is pinned as there are no official releases of the Rust runtime as of 17/10/2020.\nlambda_http = { git = \"https://github.com/awslabs/aws-lambda-rust-runtime/\", rev = \"c36409c5\"}\nrand = \"0.7.3\"\nrouterify = \"1.1.4\"\nrouterify-cors = \"1.1\"\nserde = { version = \"1.0\", features = [\"std\", \"derive\"] }\nserde_json = \"1.0\"\ntokio = { version = \"0.2\", features = [\"full\"] }\nurl = { version = \"2.1.1\", features = [\"serde\"] }\n```\n\nImport all this important stuff:\n\n```rust\nuse hyper::{Client, Server};\nuse lambda_http::{\n    handler,\n    lambda::{self, Context},\n    Body, IntoResponse, Request, RequestExt, Response,\n};\nuse rand::Rng;\nuse routerify::{Router, RouterService};\nuse std::convert::Infallible;\nuse std::{net::SocketAddr, str::FromStr};\nuse tokio::sync::oneshot;\nuse url;\n```\n\nCreate an entrypoint function using the tokio async-runtime:\n\n```rust\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), Error\u003e {\n    lambda::run(handler(start)).await?;\n    Ok(())\n}\n```\n\nCreate an alias for the type of async errors dealt with by Hyper and Routerify:\n\n```rust\ntype Error = Box\u003cdyn std::error::Error + Sync + Send + 'static\u003e;\n```\n\nDefine a handler entrypoint for the Lambda function. The Lambda function must be integrated with a resource in API Gateway, and therefore must receive a API Gateway response and return a API Gateway response,\n\n```rust\nasync fn start(req: lambda_http::Request, _ctx: Context) -\u003e Result\u003cimpl IntoResponse, Error\u003e {\n  ...\n}\n```\n\nThe function will:\n\n1. Receive an API Gateway event when the function is invoked,\n\n```rust\nasync fn start(req: lambda_http::Request, _ctx: Context) -\u003e Result\u003cimpl IntoResponse, Error\u003e {\n    ...\n}\n```\n\n2. Convert this event from a lambda_http::Request into a `hyper::Request`, the type expected by our routing library Routerify.\n\n```rust\n    // Store a copy of the query parameters, since AWS Lambda parsed these already.\n    let query_params = req.query_string_parameters();\n    // Convert the lambda_http::Request into a hyper::Request.\n    let (mut parts, body) = req.into_parts();\n    let body = match body {\n        lambda_http::Body::Empty =\u003e hyper::Body::empty(),\n        lambda_http::Body::Text(t) =\u003e hyper::Body::from(t.into_bytes()),\n        lambda_http::Body::Binary(b) =\u003e hyper::Body::from(b),\n    };\n    // Prefix the local Routerify server's address to the path of the incoming Lambda request.\n    let mut uri = format!(\"http://{}{}\", SERVER_ADDR, parts.uri.path());\n    // AWS Lambda Rust Runtime will automatically parse the query params *and* remove those\n    // query parameters from the original URI. This is fine if you're writing your logic directly\n    // in the handler function, but for passing-through to a separate router library, we need to\n    // re-url-encode the query parameters and place them back into the URI.\n    if !query_params.is_empty() {\n        uri += \"?\";\n        // Create a peekable iterator over the query parameters. This is used to add \"\u0026\" in between\n        // each of the query parameters, but prevents adding an extraneous \"\u0026\" at the end of the\n        // query parameter string.\n        let mut params = query_params.iter().peekable();\n        while let Some((key, value)) = params.next() {\n            uri += url::form_urlencoded::Serializer::new(String::new())\n                .append_pair(key, value)\n                .finish()\n                .as_str();\n            // If this is not the last parameter, append a \"\u0026\" for the next parameter...\n            if params.peek().is_some() {\n                uri += \"\u0026\";\n            }\n        }\n    }\n    parts.uri = match hyper::Uri::from_str(uri.as_str()) {\n        Ok(uri) =\u003e uri,\n        Err(e) =\u003e panic!(format!(\"failed to build uri: {:?}\", e)),\n    };\n    let req = hyper::Request::from_parts(parts, body);\n```\n\n3. Process the request through our Routerify-based HTTP program,\n\n```rust\n    // Generate some random state and build the HTTP router.\n    let router = router(State{ count: rand::thread_rng().gen::\u003cu8\u003e() });\n    // Start a internal Routerify server with the above router.\n    let serve = serve(router).await;\n    // Send the request to the routerify server and return the response.\n    let resp = Client::new().request(req).await.unwrap();\n    // Shutdown the Routerify server.\n    serve.shutdown();\n```\n\n4. Convert the result from an HTTP response (`hyper::Response`) into a API Gateway response (`lambda_http::Response`).\n\n```rust\n    // Convert the hyper::Response into a lambda_http::Response.\n    let (parts, body) = resp.into_parts();\n    let body_bytes = hyper::body::to_bytes(body).await?;\n    let body = String::from_utf8(body_bytes.to_vec()).unwrap();\n    Ok(lambda_http::Response::from_parts(parts, lambda_http::Body::from(body)))\n```\n\n### Is it really that simple?\n\nYes... _yes, it is_. 🤯\n\n## What is Routerify?\n\n[Routerify](https://github.com/routerify/routerify) is a modular implementation of an HTTP router.\n\nRouterify's main features:\n\n* 📡 Supports complex, parameterized routing logic with stateful handlers and middleware chains,\n* 🚀 Has a performant implementation based on [hyper](https://github.com/hyperium/hyper) and performs routing using [`RegexSet`](https://docs.rs/regex/1.3.9/regex/struct.RegexSet.html),\n* 🍗 Well documented with examples,\n\n\n### The steps to create a Routerify server\n\nCreate a builder function for \"building\" your Routerify router:\n\n```rust\nfn router(state: State) -\u003e Router\u003cBody, Infallible\u003e {\n    // NOTE: We have not defined `get_count`, which is the function which handles requests at this endpoint.\n    Router::builder().data(state).get(\"/data\", get_count).build().unwrap()\n}\n```\n\nDefine a struct which defines what runtime-initialized state your application requires:\n\n```rust\nstruct State {\n    count: u8\n}\n```\n\nCreate an async handler function for the `GET /data` endpoint of our API. \n\nNote about the return type: this API always returns a Result, thus the error is marked as [Infallible](https://doc.rust-lang.org/beta/std/convert/enum.Infallible.html). However, Hyper's type definitions still require a Result type to be returned.\n\nUse the appropriate HTTP status code and always return an `Ok(..)` response, where the body and headers are updated with the appropriate data.\n\n```rust\nasync fn get_count(req: Request\u003cBody\u003e) -\u003e Result\u003cResponse\u003cBody\u003e, Infallible\u003e {\n    // Access the app state.\n    let state = req.data::\u003cState\u003e().unwrap();\n    Ok(Response::builder()\n        .status(hyper::StatusCode::OK)\n        .body(Body::from(format!(\"Count: {}\", state.count))))\n}\n```\n\n## The Glue Between AWS Lambda and Routerify\n\nIn our `start` entrypoint handler, we setup a server in the AWS Lambda instance with our Routerify server:\n\n```rust\n    // Generate some random state and build the HTTP router.\n    let router = router(State{ count: rand::thread_rng().gen::\u003cu8\u003e() });\n\n    // Start an internal Routerify server with the above router.\n    let serve = serve(router).await;\n```\n\nThis function `serve` will bind a `routerify::Router` to the instance's localhost:\n\n```rust\nimpl Serve {\n    pub fn addr(\u0026self) -\u003e SocketAddr {\n        self.addr\n    }\n\n    pub fn shutdown(self) {\n        self.tx.send(()).unwrap();\n    }\n}\n\npub async fn serve\u003cB, E\u003e(router: Router\u003cB, E\u003e) -\u003e Serve\n    where\n        B: hyper::body::HttpBody + Send + Sync + Unpin + 'static,\n        E: std::error::Error + Send + Sync + Unpin + 'static,\n        \u003cB as hyper::body::HttpBody\u003e::Data: Send + Sync + 'static,\n        \u003cB as hyper::body::HttpBody\u003e::Error: std::error::Error + Send + Sync + 'static,\n{\n    let service = RouterService::new(router).unwrap();\n    let server = Server::bind(\u0026([127, 0, 0, 1], 0).into()).serve(service);\n    let addr = server.local_addr();\n\n    let (tx, rx) = oneshot::channel::\u003c()\u003e();\n\n    let graceful_server = server.with_graceful_shutdown(async {\n        rx.await.unwrap();\n    });\n\n    tokio::spawn(async move {\n        graceful_server.await.unwrap();\n    });\n\n    Serve { addr, tx }\n}\n```\n\nWe can then serve the request to the server's local address, await the response, then shutdown the Routerify server (as we only serve one request per AWS Lambda instance).\n\n```rust\n    // Prefix the local Routerify's address to the path of the incoming Lambda request.\n    let uri = format!(\"http://{}{}\", serve.addr(), parts.uri.path());\n    parts.uri = hyper::Uri::from_str(uri.as_str()).unwrap();\n    let req = hyper::Request::from_parts(parts, body);\n\n    // Send the request to the routerify server and return the response.\n    let resp = Client::new().request(req).await.unwrap();\n\n    // Shutdown the Routerify server.\n    serve.shutdown();\n```\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseanpianka%2Frust-routerify-aws-lambda-quickstart","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseanpianka%2Frust-routerify-aws-lambda-quickstart","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseanpianka%2Frust-routerify-aws-lambda-quickstart/lists"}