Environment Variables and Stages
Controlling your code's behavior based on the environment.
What you'll learn
- Using Parameters in template.yaml
- Passing Environment Variables to Lambda
- Reading environment variables in Rust
You should never hardcode values like database names, API keys, or stage-specific settings directly into your code. Instead, you should use Environment Variables. This allows you to use the exact same code in dev, staging, and prod, but with different configurations.
Stages in SAM
In lesson 5, we added a StageName parameter. Now let's see how to pass that value down to our Rust code.
Parameters:
StageName:
Type: String
Default: dev
Globals:
Function:
Runtime: provided.al2023
Environment:
Variables:
STAGE: !Sub ${StageName}The Globals section is a shortcut. By putting the Environment section here, every Lambda function in this project will automatically get an environment variable named STAGE.
Reading Variables in Rust
In Rust, we use the standard library std::env to read these variables.
Update your handler in src/bin/GreetingManager.rs:
use std::env;
async fn handler(
event: LambdaEvent<ApiGatewayProxyRequest>,
) -> Result<ApiGatewayProxyResponse, Error> {
// Read the STAGE variable. Default to "dev" if it's missing.
let stage = env::var("STAGE").unwrap_or_else(|_| "dev".to_string());
let (method, resource) = get_route(&event.payload);
match (method.as_str(), resource.as_str()) {
("GET", "/hello") => {
let message = format!("Hello from the {} environment!", stage);
Ok(create_api_response(200, &message, Some(())))
}
// ...
}
}Why use stages?
Imagine you have a Users table in DynamoDB.
- In
dev, the table is namedusers-dev. - In
prod, the table is namedusers-prod.
Instead of checking if stage == "prod" in your Rust code, you can just pass the table name as an environment variable in template.yaml:
Environment:
Variables:
USER_TABLE: !Sub "users-${StageName}"Then in Rust:
let table_name = env::var("USER_TABLE").expect("USER_TABLE not set");This keeps your Rust code clean and "agnostic" of the environment it's running in.
If you try to read a variable that doesn't exist, env::var will return an Err.
- Use
.expect("msg")if the variable is required (the Lambda will crash if it's missing, which is usually better than running with bad config). - Use
.unwrap_or(...)if the variable has a sensible default.
Pro Tip
You can also use a crate like dotenvy for local development, but for AWS Lambda, the built-in std::env is all you need.
Our configuration is solid, but what happens when something actually goes wrong? In the next post, we'll dive deep into Error Handling the "Rust Way" to make our API bulletproof.