Context
Goal: Own the pipeline and keep ops near-zero. Requirements: fast UX, high deliverability, abuse protection, and no third‑party form service. Constraints: keep bundle small and preserve Web Vitals (LCP ≤ 2.0s, INP ≤ 200ms).
Architecture
- CloudFront (edge) → API Gateway HTTP API → Lambda (Node.js 20)
- DynamoDB table
contact_rate_limits
with TTL for per‑IP windowing - Amazon SES (domain verified, DKIM/SPF aligned) for delivery
- S3 static site; CloudFront Function injects security headers


Decisions & Trade-offs
- HTTP API vs REST API: chose HTTP API for lower latency/cost; fewer built-ins like usage plans.
- DynamoDB TTL vs Redis: serverless and durable; slightly higher p99 than in-memory.
- Direct SES send vs SNS fan‑out: fewer moving parts now; SNS can be added later for archive/Slack.
- Single region: simpler/cheaper today; multi‑region is future work if the form becomes critical-path.
Results
Latency P95 ~320 ms
Delivery >99%
Cost/mo <$0.10
Uptime 99.99%
Measured via curl + CloudWatch logs sampling; replace with screenshots as you gather more data.
Cost & Ops
- API Gateway + Lambda: effectively free at current volume; DynamoDB on‑demand for a single read/write per submission.
- SES out of sandbox; monitor bounces/complaints. Ensure DMARC policy is aligned.
- CloudWatch alarms on 5XX rate and p95 latency; structured logs for request IDs and rate‑limit hits.
Improvements
- Add Cloudflare Turnstile or reCAPTCHA at higher traffic.
- EventBridge for async fan‑out (archive to S3, Slack/Email multi-destination).
- Promote to CDK stack bundling API, Lambda, DDB, and CF Function.
- Optional multi‑region API + Route 53 health checks.