Deploy a serverless API with Lambda and API Gateway
Write a Python Lambda function that accepts a JSON POST body, validates it, performs a computation or external API call, and returns a structured response. Expose the function via API Gateway HTTP API, deploy the full stack with Terraform, and verify end-to-end with a curl request.
Why this matters
Serverless is the default architecture for low-to-medium traffic APIs in AWS. Lambda removes the operational burden of servers, scales to zero when idle, and costs nothing when unused. The Terraform-managed approach you build here is how production serverless stacks are managed; nothing clicked in the console, everything reproducible from code.
Before you start
- AWS account with Lambda and API Gateway permissions
- Terraform installed and AWS CLI configured
- Python 3.12 (the current Lambda runtime)
- Basic curl or Postman knowledge for testing
Step-by-step guide
- 1
Write the Lambda handler
Create handler.py with a lambda_handler(event, context) function. Parse event['body'] as JSON, validate the required fields, perform the logic, and return {statusCode: 200, body: json.dumps(result)}. Test locally by calling lambda_handler({body: '{...}'}, None); if it works locally, it will work in Lambda.
- 2
Package the deployment zip
Create a deployment package: zip handler.zip handler.py. If you have dependencies, pip install them into a local directory and include that directory in the zip. The zip structure matters; the handler file must be at the root, not in a subdirectory.
- 3
Write the Terraform
Define aws_iam_role for the Lambda execution role with AWSLambdaBasicExecutionRole. Define aws_lambda_function pointing at your zip. Define aws_apigatewayv2_api (HTTP API), aws_apigatewayv2_integration, and aws_apigatewayv2_route. Output the API endpoint URL.
- 4
Deploy and test
Run terraform apply. Copy the output URL. Run curl -X POST with your JSON body. If you get a 502, check the Lambda CloudWatch logs; the most common cause is a JSON serialisation error in the response body. The response body must be a string, not a dict.
- 5
Add environment variables
Update the Terraform to pass environment variables to the Lambda. Read them in the handler with os.environ. Verify they are present at runtime. Never hardcode secrets in the zip; use environment variables referencing AWS Secrets Manager or Parameter Store.
- 6
Measure cold start
Invoke the Lambda immediately after deployment (cold start), then again within 60 seconds (warm start). Measure the difference. For Python 3.12 with no dependencies, a cold start should be under 300ms. Over 1 second usually means too many imports at the top of the handler file.