TutorialbeginnerPart 10 of 18

Path Parameters and Query Strings

Making your API dynamic by reading input from the URL.

April 21, 20263 min read

What you'll learn

  • Extracting path parameters from the event
  • Reading query string parameters
  • Creating multiple binaries in one project
Prerequisites:Previous post completed (Building and Deploying)

A real API needs to handle input. Whether it's a user ID in the URL (/users/123) or a search term in a query string (/search?q=rust), your Lambda needs to know how to read these values.

Adding a dynamic handler

Instead of stuffing everything into hello.rs, we're going to create a new file specifically for a "Hello Name" feature. This demonstrates how you can have multiple Lambda functions in a single Rust project.

Create a new file at src/bin/hello_name.rs:

src/bin/hello_name.rs
rust
use aws_lambda_events::apigw::{ApiGatewayProxyRequest, ApiGatewayProxyResponse};
use aws_lambda_events::encodings::Body;
use aws_lambda_events::http::HeaderMap;
use lambda_runtime::{run, service_fn, Error, LambdaEvent};
 
async fn handler(event: LambdaEvent<ApiGatewayProxyRequest>) -> Result<ApiGatewayProxyResponse, Error> {
    // 1. Extract path parameters
    let path_params = &event.payload.path_parameters;
    let name = path_params.get("name").unwrap_or(&"Guest".to_string());
 
    // 2. Extract query parameters (optional)
    let query_params = &event.payload.query_string_parameters;
    let title = query_params.get("title").map(|v| format!("{} ", v)).unwrap_or_default();
 
    let message = format!("Hello, {}{}!", title, name);
 
    let body = serde_json::json!({
        "statusCode": 200,
        "message": message,
        "data": null
    });
 
    let mut headers = HeaderMap::new();
    headers.insert("Content-Type", "application/json".parse().unwrap());
 
    let mut response = ApiGatewayProxyResponse::default();
    response.status_code = 200;
    response.headers = headers;
    response.body = Some(Body::Text(serde_json::to_string(&body)?));
 
    Ok(response)
}
 
#[tokio::main]
async fn main() -> Result<(), Error> {
    run(service_fn(handler)).await
}

Updating Cargo.toml

We need to tell Rust about this new binary. Update your Cargo.toml:

Cargo.toml
toml
[[bin]]
name = "hello"
path = "src/bin/hello.rs"
 
[[bin]]
name = "hello_name"
path = "src/bin/hello_name.rs"

Updating template.yaml

Now we need to tell AWS to create a second Lambda function and connect it to a new route.

template.yaml
yaml
# ... (Globals and API definition) ...
 
Resources:
  # ... (HelloFunction and GetHelloMethod) ...
 
  # The New Route: /hello/{name}
  HelloNameResource:
    Type: AWS::ApiGateway::Resource
    Properties:
      ParentId: !Ref HelloResource
      PathPart: '{name}'
      RestApiId: !Ref RustExample
 
  # The New Lambda
  HelloNameFunction:
    Type: AWS::Serverless::Function
    Metadata:
      BuildMethod: rust-cargolambda
    Properties:
      CodeUri: .
      Handler: bootstrap
      Runtime: provided.al2023
      Architectures: ["arm64"]
 
  # The New Method
  GetHelloNameMethod:
    Type: AWS::ApiGateway::Method
    Properties:
      RestApiId: !Ref RustExample
      ResourceId: !Ref HelloNameResource
      AuthorizationType: NONE
      HttpMethod: GET
      Integration:
        Type: AWS_PROXY
        IntegrationHttpMethod: POST
        Uri: !Sub "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HelloNameFunction.Arn}/invocations"
 
  # Permission for the new function
  HelloNameFunctionApiPermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !Ref HelloNameFunction
      Action: lambda:InvokeFunction
      Principal: apigateway.amazonaws.com
      SourceArn: !Sub "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${RustExample}/*"

Info

Notice that PathPart is {name}. The curly braces tell API Gateway that this is a dynamic variable. This value is what shows up in the path_parameters map in our Rust code.

Deploying again

Since we added a new binary and resources, we need to build and deploy:

Terminal
bash
sam build
sam deploy

Now you can test it: curl https://your-api.com/dev/hello/Hazeezet Response: {"message": "Hello, Hazeezet!"}

curl https://your-api.com/dev/hello/Hazeezet?title=Dev Response: {"message": "Hello, Dev Hazeezet!"}

This is working great, but our response structure is a bit messy and repetitive. In the next post, we'll create a standardized JSON response structure to make our API professional.