1. Lambda Functions, Cold Starts & Event Sources
A Lambda function is a single handler AWS runs in response to an event and stops — you're billed only for the milliseconds it actually executes, and nothing runs (or costs anything) between invocations. That's the opposite trade-off from the always-on EC2/Kubernetes model of Weeks 8–17: no idle cost, but also no persistent process to hold a warm database connection pool or in-memory cache between requests.
import json
def 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}"})
}
zip function.zip handler.py
aws lambda create-function \
--function-name hello-api \
--runtime python3.12 \
--role arn:aws:iam::123456789012:role/lambda-basic-execution \
--handler handler.handler \
--zip-file fileb://function.zip \
--timeout 10 \
--memory-size 256
A cold start happens when Lambda has to initialize a fresh execution environment — download your code, start the runtime, run any top-level initialization — before it can handle the first request; a warm invocation reuses an already-initialized environment and skips all of that. Cold starts are the single biggest latency variable in Lambda: typically tens to a few hundred milliseconds for a small interpreted-language function, but they can reach seconds for a large function with a heavy runtime and lots of top-level imports.
A function can be triggered by more than an HTTP request — an S3 upload, a message
landing in an SQS queue, a scheduled EventBridge rule, or a change stream from
DynamoDB are all common event sources, each delivering a
differently-shaped event object to the same handler signature.
Opening a fresh connection on every invocation adds real latency and can exhaust a database's connection limit under concurrent load. Initialize the connection at module scope, outside the handler function — on a warm invocation, that code doesn't re-run, so the connection is reused across requests, and pairing it with RDS Proxy handles the connection pooling problem at scale.
2. API Gateway Routes & Request Mapping
A Lambda function has no URL of its own — API Gateway is what turns it into an HTTP API, routing incoming requests to the right function and translating its response back into a proper HTTP response. The modern, cheaper option for a straightforward REST-style API is an HTTP API (as opposed to the older, more configurable REST API type).
aws apigatewayv2 create-api \
--name hello-api \
--protocol-type HTTP \
--target arn:aws:lambda:us-east-1:123456789012:function:hello-api
# grant API Gateway permission to invoke the function
aws lambda add-permission \
--function-name hello-api \
--statement-id apigw-invoke \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com
With Lambda proxy integration (the default and recommended mode),
API Gateway passes the entire raw request — method, path, headers, query string,
body — into the event object exactly as seen in Section 1, and expects
the function's return value to fully specify the HTTP response, including
statusCode and headers. The alternative,
non-proxy integration lets API Gateway itself transform the request and response
shape with velocity-template mapping — more configuration, rarely worth it for a new
API, but worth recognizing if you inherit one.
Without a configured rate limit, a runaway client or a bug in an upstream caller can drive unlimited concurrent Lambda invocations — and unlimited cost — against your account. API Gateway's per-route throttling and Lambda's own reserved/provisioned concurrency settings are the equivalent of the resource limits from Week 12's Deployments: a deliberate ceiling, not an assumption that traffic will stay reasonable.
3. Fargate: Containers Without Managing Nodes
Lambda is a poor fit for a long-running process, a large container image, or a workload that needs more than 15 minutes to complete (Lambda's hard execution limit). Fargate covers that gap: it's a launch type for ECS (or EKS) that runs the exact same Docker images from Week 3–5 without you provisioning or patching any EC2 instance — you specify CPU and memory for the task, and AWS runs the container on infrastructure it manages entirely.
{
"family": "web-app",
"requiresCompatibilities": ["FARGATE"],
"networkMode": "awsvpc",
"cpu": "512",
"memory": "1024",
"containerDefinitions": [
{
"name": "web-app",
"image": "123456789012.dkr.ecr.us-east-1.amazonaws.com/web-app:v1.4.0",
"portMappings": [{ "containerPort": 8000, "protocol": "tcp" }],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/web-app",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "web-app"
}
}
}
]
}
aws ecs register-task-definition --cli-input-json file://task-definition.json
aws ecs create-service \
--cluster production \
--service-name web-app \
--task-definition web-app \
--desired-count 2 \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[subnet-abc123],securityGroups=[sg-xyz789],assignPublicIp=ENABLED}" \
--load-balancers "targetGroupArn=arn:aws:elasticloadbalancingv2:...,containerName=web-app,containerPort=8000"
This is the same container, the same image, the same
desired-count: 2 instinct as the Kubernetes Deployments from Week 12 —
Fargate is fundamentally the same "run N replicas of this container behind a load
balancer" idea, just without a cluster of nodes or a control plane to operate. The
trade-off runs the other direction from Kubernetes: less operational surface, but
also less of the ecosystem — no Helm charts, no custom Operators, no service mesh
sidecar injection.
An event-driven function that runs in milliseconds and idles most of the time belongs on Lambda. A handful of long-running containerized services without the operational need for Kubernetes' extensibility belongs on Fargate. A system genuinely needing StatefulSets, Operators, a service mesh, or portability across clouds belongs on Kubernetes — the right choice tracks the actual workload, and being able to justify it either way is what an interviewer is listening for.
4. Hands-on Exercise
Ship one endpoint on Lambda and one service on Fargate
Build both ends of the serverless spectrum and compare them directly.
Requirements:
- Deploy a Lambda function behind an HTTP API Gateway route, and measure its cold-start latency vs. warm-invocation latency with a simple timing script.
- Add an S3 event source triggering a second Lambda whenever an object is uploaded to a bucket, and confirm it fires by uploading a test file.
- Register a Fargate task definition for a containerized app you built in an earlier week, and run it as an ECS service with 2 desired tasks behind an Application Load Balancer.
- Write a one-paragraph comparison: for your specific app, would Lambda, Fargate, or the Kubernetes cluster from Weeks 11–17 be the right production choice, and why?
CloudWatch Logs Insights query filter @type = "REPORT" | stats avg(@duration), max(@duration) by bin(5m) against your function's log group is a quick way to see cold-start-driven latency spikes without instrumenting anything yourself.
5. Knowledge Check
Three quick questions. Expand each to check your answer.
Q1
Why should a database connection be initialized outside the Lambda handler function, not inside it?
Why should a database connection be initialized outside the Lambda handler function, not inside it?
Code at module scope only runs once per execution environment, on a cold start; the handler function runs on every invocation. Opening the connection inside the handler means re-establishing it on every single request even when the environment is warm, adding latency and risking exhausting the database's connection limit under load — initializing it at module scope lets a warm invocation reuse the existing connection.
Q2
With Lambda proxy integration, what is API Gateway's role versus the function's role in shaping the HTTP response?
With Lambda proxy integration, what is API Gateway's role versus the function's role in shaping the HTTP response?
API Gateway passes the raw request through unchanged and does no transformation of its own; the Lambda function's return value is expected to fully specify the response, including statusCode, headers, and body. This is the opposite of non-proxy integration, where API Gateway itself applies mapping templates to reshape the request and response.
Q3
What's the actual trade-off between choosing Fargate and choosing the Kubernetes cluster from Weeks 11–17 for the same containerized app?
What's the actual trade-off between choosing Fargate and choosing the Kubernetes cluster from Weeks 11–17 for the same containerized app?
Fargate runs the same container images with far less operational surface — no nodes, no control plane, no cluster to patch — at the cost of losing Kubernetes' extensibility: no Helm charts, custom Operators, or service mesh sidecar injection. The right choice depends on whether the workload genuinely needs that ecosystem (StatefulSets, Operators, portability across clouds) or is better served by fewer moving parts.