Skip to main content
Back to projects

serverless-data-api — the boring layers tutorials skip

Production-grade serverless CRUD in Terraform: IAM least-privilege, API-key usage plans, PITR, and a teardown that leaves zero orphans.

TerraformPythonAWS Lambda · arm64API GatewayDynamoDB · single-tableCloudWatchGitHub Actions
Access control
IAM least-privilege scoped to the table ARN
Resilience
DynamoDB PITR · teardown leaves 0 orphans
Provisioned
15 resources · 5 reusable modules
Idle cost
$0/month (no dashboard)

The problem

Serverless tutorials skip the boring layers: IAM least-privilege, API-key + usage-plan auth, structured logging, a teardown story. The result is a demo that can't reach production without a rewrite.

The solution

Five composable Terraform modules (dynamodb, iam, lambda, api_gateway, monitoring) wire 15 resources end to end. The Lambda execution role is least-privilege scoped to the table's ARN. The API Gateway usage plan caps at 1000 req/day with burst 10. GitHub Actions runs fmt + validate + plan on every PR. Idle cost is $0 (or $3/month with the CloudWatch dashboard kept on).

fig. 01decision record
Constraint
A serverless API that can be cloned straight into production — IAM least-privilege, auth, observability, and a clean teardown — not a demo that needs a rewrite first.
Decision
Scope the Lambda role to the table ARN (never a wildcard), gate the API behind an API-key usage plan, cap at 1000 req/day, and make `terraform destroy` leave zero orphans. Provision every layer as a reusable module so the pattern is copyable.
Outcome
15 resources across 5 modules, least-privilege IAM, PITR, and a teardown that removes everything — at $0/month idle.

Overview

A production-ready serverless CRUD API provisioned entirely via Terraform. API Gateway with API-key auth and 1000-req/day rate limiting fronts a Python 3.12 Lambda on arm64 with AWS Powertools, backed by a single-table DynamoDB design with PITR. A CloudWatch dashboard with 8 widgets covers invocations, latency, errors, and capacity. `terraform apply` brings it up; `terraform destroy` removes every resource — no orphans.