An AWS Lambda + API Gateway serverless architecture lets you run a REST API with zero servers to patch or scale — API Gateway handles routing and auth, Lambda executes your business logic on demand, and you pay only for actual invocations. This guide walks through building one end to end: function, API, IAM role, and deployment.
Key Takeaways
- API Gateway handles HTTP routing, request validation, throttling, and auth; Lambda holds your business logic.
- Lambda needs an execution role with least-privilege permissions — not broad admin access.
- Lambda proxy integration forwards the full request to your function and lets your code control the response shape.
- Use stages (dev/prod) and custom domain + Route 53 for production-grade API versioning and branding.
- Cold starts matter for latency-sensitive APIs — mitigate with provisioned concurrency on hot paths, not everywhere.
Step 1: Write the Lambda Function
# handler.py — a simple Lambda handler for API Gateway proxy integration
import json
def lambda_handler(event, context):
name = event.get("queryStringParameters", {}).get("name", "World")
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({"message": f"Hello, {name}!"})
}
Package and prepare for deployment:
# Zip the function for deployment
zip function.zip handler.py
Step 2: Create the Execution Role (Least Privilege)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
}
]
}
# Create the role and attach the policy above as an inline policy
aws iam create-role \
--role-name lambda-api-exec-role \
--assume-role-policy-document file://trust-policy.json
aws iam put-role-policy \
--role-name lambda-api-exec-role \
--policy-name lambda-logging \
--policy-document file://exec-policy.json
Only add extra permissions (DynamoDB, S3, etc.) as your function actually needs them — starting from CloudWatch Logs-only access is the correct default.
Step 3: Deploy the Function and Wire Up API Gateway
# Create the Lambda function
aws lambda create-function \
--function-name greet-api \
--runtime python3.12 \
--role arn:aws:iam::123456789012:role/lambda-api-exec-role \
--handler handler.lambda_handler \
--zip-file fileb://function.zip
# Create a REST API and a proxy resource/method pointing at the Lambda
aws apigateway create-rest-api --name "greet-api-gw"
# (capture the returned API id as $API_ID)
aws apigateway get-resources --rest-api-id $API_ID
# (capture root resource id as $ROOT_ID)
aws apigateway create-resource \
--rest-api-id $API_ID --parent-id $ROOT_ID --path-part "greet"
aws apigateway put-method \
--rest-api-id $API_ID --resource-id $RESOURCE_ID \
--http-method GET --authorization-type NONE
aws apigateway put-integration \
--rest-api-id $API_ID --resource-id $RESOURCE_ID \
--http-method GET --type AWS_PROXY --integration-http-method POST \
--uri arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:greet-api/invocations
# Grant API Gateway permission to invoke the Lambda function
aws lambda add-permission \
--function-name greet-api \
--statement-id apigateway-invoke \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:us-east-1:123456789012:$API_ID/*/GET/greet"
Terraform Version (Recommended for Production)
resource "aws_lambda_function" "greet" {
function_name = "greet-api"
runtime = "python3.12"
handler = "handler.lambda_handler"
filename = "function.zip"
role = aws_iam_role.lambda_exec.arn
}
resource "aws_apigatewayv2_api" "greet_api" {
name = "greet-api-gw"
protocol_type = "HTTP"
}
resource "aws_apigatewayv2_integration" "lambda" {
api_id = aws_apigatewayv2_api.greet_api.id
integration_type = "AWS_PROXY"
integration_uri = aws_lambda_function.greet.invoke_arn
payload_format_version = "2.0"
}
resource "aws_apigatewayv2_route" "get_greet" {
api_id = aws_apigatewayv2_api.greet_api.id
route_key = "GET /greet"
target = "integrations/${aws_apigatewayv2_integration.lambda.id}"
}
resource "aws_apigatewayv2_stage" "prod" {
api_id = aws_apigatewayv2_api.greet_api.id
name = "$default"
auto_deploy = true
}
resource "aws_lambda_permission" "apigw" {
statement_id = "AllowAPIGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.greet.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_apigatewayv2_api.greet_api.execution_arn}/*/*"
}
HTTP APIs (aws_apigatewayv2_api) are cheaper and lower-latency than REST APIs for most use cases — use REST APIs only when you need features HTTP APIs lack (request validation, API keys/usage plans, WAF integration).
Request Flow Architecture
<svg viewBox="0 0 740 260" xmlns="http://www.w3.org/2000/svg" style="width:100%;max-width:740px;font-family:sans-serif;color:currentColor">
<style>
.box { fill: none; stroke: currentColor; stroke-width: 1.5; }
.lbl { fill: currentColor; font-size: 12px; }
.accent { fill: currentColor; opacity: 0.12; }
.arrow { stroke: currentColor; stroke-width: 1.3; marker-end: url(#a4); }
</style>
<defs>
<marker id="a4" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto">
<path d="M0,0 L8,3 L0,6 Z" fill="currentColor"/>
</marker>
</defs>
<rect x="20" y="100" width="120" height="50" class="box accent"/>
<text x="35" y="130" class="lbl">Client</text>
<rect x="220" y="100" width="150" height="50" class="box"/>
<text x="235" y="125" class="lbl">API Gateway</text>
<text x="235" y="142" class="lbl" style="font-size:10px">auth, throttling, routing</text>
<rect x="450" y="100" width="130" height="50" class="box accent"/>
<text x="465" y="130" class="lbl">Lambda</text>
<rect x="650" y="100" width="130" height="50" class="box" transform="translate(-60,0)"/>
<text x="605" y="125" class="lbl">DynamoDB /</text>
<text x="605" y="142" class="lbl">other services</text>
<line x1="140" y1="125" x2="220" y2="125" class="arrow"/>
<line x1="370" y1="125" x2="450" y2="125" class="arrow"/>
<line x1="580" y1="125" x2="660" y2="125" class="arrow"/>
</svg>
Common Mistakes in Lambda + API Gateway Setups
- Overly broad IAM roles — attaching
AdministratorAccessto a Lambda role instead of scoping permissions to exact resources/actions needed. - No request validation — letting malformed requests reach Lambda wastes invocations; validate at the API Gateway layer with a JSON schema model.
- Ignoring cold starts on latency-critical paths — a checkout API in Python/Node with no provisioned concurrency can add 300ms+ tail latency under low traffic.
- Not setting a timeout matched to the integration — API Gateway has a hard 29-second timeout; a Lambda configured for 60s will still get cut off by the gateway.
FAQ
Is API Gateway required to use Lambda as an API? Not strictly — Lambda Function URLs offer a simpler direct HTTPS endpoint — but API Gateway adds throttling, custom domains, request validation, usage plans, and WAF integration that most production APIs need.
How do I reduce Lambda cold start latency? Use a lighter runtime (Node.js/Python over Java typically), keep deployment package size small, and apply provisioned concurrency to functions on latency-sensitive, low-traffic paths.
What's the difference between HTTP API and REST API in API Gateway? HTTP APIs (API Gateway v2) are cheaper and faster for simple proxy integrations; REST APIs (v1) support more features like request/response transformation, API keys, and usage plans — use REST only if you need those.
How do I secure a Lambda-backed API? Use an IAM authorizer, a Lambda authorizer (custom token logic), or a Cognito User Pool authorizer at the API Gateway layer — never rely on Lambda-internal checks alone, since that still exposes the invocation to unauthenticated traffic.
Build a Production-Ready Serverless API with F9 Infotech
Going from a working prototype to a production serverless API — proper IAM scoping, cold start mitigation, observability, and CI/CD — takes real AWS architecture experience. F9 Infotech's serverless engineering team can design, build, and operate your Lambda + API Gateway architecture end to end. Talk to our AWS serverless team about your API project.