A Serverless "Hello, From Lambda!" with AWS Lambda
Build and deploy your first AWS Lambda function from scratch — function handler, test events, CloudWatch logs, and an API Gateway trigger.
October 21, 2021 · 3 min read · By Kshitiz Regmi
"AWS Lambda lets you run code without thinking about servers." AWS handles scaling, patching, and availability automatically — you focus purely on your function logic.
Why Go Serverless?
Traditional backend infrastructure requires provisioning servers, configuring load balancers, managing OS patches, and monitoring uptime 24/7. AWS Lambda eliminates all of that:
- No servers to manage — AWS runs your code on demand
- Auto-scaling — from 1 to thousands of concurrent executions automatically
- Pay per use — billed by 1ms increments, only when your code runs
- Event-driven — triggered by API calls, S3 uploads, DynamoDB streams, cron schedules, and more
Creating Your First Lambda Function
Step 1: Open the Lambda Console
- Log into the AWS Console and search for Lambda
- Click Create function
- Choose Author from scratch
Step 2: Configure
- Function name:
hello-lambda - Runtime: Python 3.11
- Architecture: x86_64
Leave the default execution role — AWS creates one with CloudWatch logging permissions automatically.
Step 3: Write the Handler
import json
def lambda_handler(event, context):
print("Received event:", json.dumps(event, indent=2))
name = event.get("name", "World")
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({
"message": f"Hello, From Lambda! Hi {name}",
"requestId": context.aws_request_id
})
}
The lambda_handler(event, context) signature is Lambda's entry point:
event: input data (dict) from the trigger — API Gateway, S3, EventBridge, etc.context: runtime metadata — function name, remaining time, request ID
Step 4: Test the Function
Click Test → Create new test event:
{
"name": "Kshitiz"
}
Run it. You'll see:
{
"statusCode": 200,
"body": "{\"message\": \"Hello, From Lambda! Hi Kshitiz\", \"requestId\": \"abc-123\"}"
}
Monitoring with CloudWatch
Every Lambda invocation streams logs to Amazon CloudWatch automatically. Navigate to Monitor → View CloudWatch Logs:
START RequestId: abc-123-def Version: $LATEST
Received event: {
"name": "Kshitiz"
}
END RequestId: abc-123-def
REPORT RequestId: abc-123-def Duration: 2.45 ms Billed Duration: 3 ms Memory Size: 128 MB Max Memory Used: 38 MB
CloudWatch gives you logs, metrics, error rates, duration percentiles, and alarm triggers — all out of the box.
Adding an API Gateway Trigger
To call your Lambda over HTTP:
- Click Add trigger → API Gateway
- Select Create a new API → REST API
- Security: Open (for testing — use auth in production)
- Click Add
AWS creates an HTTPS endpoint like:
https://abc123.execute-api.us-east-1.amazonaws.com/default/hello-lambda
Call it with curl:
curl -X POST https://abc123.execute-api.us-east-1.amazonaws.com/default/hello-lambda \
-H "Content-Type: application/json" \
-d '{"name": "Kshitiz"}'
{"message": "Hello, From Lambda! Hi Kshitiz", "requestId": "xyz-456"}
The Cold Start Problem
Lambda containers are ephemeral. The first invocation after a period of inactivity incurs a cold start — Lambda must spin up a new execution environment (~100–500ms). Subsequent invocations reuse the warm container (typically <10ms overhead).
Mitigation strategies:
- Provisioned Concurrency — pre-warm containers (costs $)
- Minimize package size — smaller deployment packages load faster
- Keep the handler lean — move heavy initialization (DB connections, model loading) outside the handler to the module level, so it runs once and is reused across warm invocations
# Good: initialized once at module load, reused across warm invocations
import boto3
s3 = boto3.client('s3')
def lambda_handler(event, context):
# s3 is already initialized — no cold-start overhead here
response = s3.get_object(Bucket="my-bucket", Key=event["key"])
return response["Body"].read().decode()
Lambda Limits
| Limit | Value |
|---|---|
| Max execution timeout | 15 minutes |
| Memory | 128 MB – 10 GB |
| Ephemeral storage (/tmp) | 512 MB – 10 GB |
| Deployment package | 50 MB (zipped) |
| Default concurrency | 1,000 (region-wide, adjustable) |
When to Use Lambda
Good fit:
- REST APIs (via API Gateway)
- Event-driven processing (S3 uploads, SQS messages)
- Scheduled jobs (cron via EventBridge)
- Lightweight ML inference
- ETL and data transformation
Not a good fit:
- Jobs exceeding 15 minutes
- Workloads needing persistent in-memory state
- High-throughput streaming requiring sub-millisecond latency