AWS with Floci
Chapter 7 — Serverless (Lambda + API Gateway)
Day 1 — Deploying and Invoking a Lambda Function
1. Concept Primer
Lambda runs your handler code in response to events, without you managing servers. Floci runs these in real Docker containers, so runtime behavior (timeouts, memory limits, cold starts) is representative rather than simulated.
2. Hands-on Exercise
Write a minimal Python handler, zip it, deploy it, and invoke it directly.
3. Exact Commands
floci start && eval $(floci env)
mkdir -p lambda-day1 && cd lambda-day1
cat > handler.py << 'EOF'
def handler(event, context):
name = event.get("name", "world")
return {"statusCode": 200, "body": f"hello, {name}"}
EOF
zip function.zip handler.py
aws iam create-role --role-name lambda-exec-role \
--assume-role-policy-document '{
"Version":"2012-10-17",
"Statement":[{"Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"},"Action":"sts:AssumeRole"}]
}'
aws lambda create-function \
--function-name hello-fn \
--runtime python3.12 \
--handler handler.handler \
--role arn:aws:iam::000000000000:role/lambda-exec-role \
--zip-file fileb://function.zip
aws lambda invoke --function-name hello-fn \
--payload '{"name":"floci"}' --cli-binary-format raw-in-base64-out \
response.json
cat response.json
4. Gotchas
- Lambda is one of Floci's strongest areas since it runs a real container per invocation — expect genuine cold-start behavior, not an instant mock response.
--cli-binary-format raw-in-base64-outis required on recent AWS CLI v2 versions forinvokepayloads, or you'll get a base64 encoding error.- The IAM role ARN's account ID (
000000000000) is a fixed placeholder in local emulators — reuse it verbatim rather than trying to look up a "real" account ID.
5. Self-Check
What's the practical benefit of Lambda running in a real Docker container locally, compared to a runtime that just mocks the response shape?
Day 2 — Wiring an S3 Event Trigger
1. Concept Primer
Lambda can be invoked automatically by event sources instead of direct invoke calls. An
S3 bucket notification is the classic example: uploading an object fires the function
with the object's bucket/key in the event payload.
2. Hands-on Exercise
Attach the Day 1 function as an S3 event trigger, upload a file, and confirm the function ran by checking its logs.
3. Exact Commands
eval $(floci env)
aws s3 mb s3://trigger-bucket
aws lambda add-permission \
--function-name hello-fn \
--statement-id s3invoke \
--action lambda:InvokeFunction \
--principal s3.amazonaws.com \
--source-arn arn:aws:s3:::trigger-bucket
FN_ARN=$(aws lambda get-function --function-name hello-fn --query 'Configuration.FunctionArn' --output text)
aws s3api put-bucket-notification-configuration \
--bucket trigger-bucket \
--notification-configuration "{
\"LambdaFunctionConfigurations\": [{
\"LambdaFunctionArn\": \"$FN_ARN\",
\"Events\": [\"s3:ObjectCreated:*\"]
}]
}"
echo "triggering upload" > trigger.txt
aws s3 cp trigger.txt s3://trigger-bucket/trigger.txt
aws logs describe-log-groups --log-group-name-prefix /aws/lambda/hello-fn
aws logs tail /aws/lambda/hello-fn --since 5m
4. Gotchas
- The handler from Day 1 doesn't parse S3 event structure — it'll still run, but
event.get("name")won't find anything meaningful in an S3-triggered payload. That's expected; the point here is proving the trigger fires, not processing the payload correctly. - If logs appear empty right after upload, wait a couple seconds and re-run
logs tail— log delivery isn't always instantaneous even locally.
5. Self-Check
If you wanted the handler to actually read the uploaded object's key, which part of the
Lambda event argument would you need to parse?
Day 3 — API Gateway in Front of Lambda
1. Concept Primer
API Gateway exposes an HTTP(S) endpoint that integrates with a backend — commonly Lambda. An HTTP API is the simpler, cheaper, lower-latency option; a REST API offers more features (request validation, usage plans, more integration types) at more complexity.
2. Hands-on Exercise
Create an HTTP API, connect it to the Day 1 Lambda, and call it with plain curl.
3. Exact Commands
eval $(floci env)
FN_ARN=$(aws lambda get-function --function-name hello-fn --query 'Configuration.FunctionArn' --output text)
API_ID=$(aws apigatewayv2 create-api \
--name hello-api --protocol-type HTTP \
--target $FN_ARN --query 'ApiId' --output text)
aws lambda add-permission \
--function-name hello-fn \
--statement-id apigw-invoke \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:us-east-1:000000000000:$API_ID/*/*"
curl "http://localhost:4566/restapis/$API_ID/\$default/_user_request_/?name=curl-user"
4. Gotchas
- The
--targetshortcut oncreate-apiauto-wires a default$defaultstage and route pointed at your Lambda — convenient for a quick demo, but production setups usually define routes/integrations explicitly for more control. - The exact local invoke URL format can differ slightly between Floci versions — if the
path above 404s, run
aws apigatewayv2 get-api --api-id $API_IDand check the returned endpoint pattern.
5. Self-Check
What's the main tradeoff between choosing an HTTP API versus a REST API for a simple Lambda-backed endpoint?