API
cd-api is a small FastAPI service with one real job: serve current_members over HTTP for cd-lookup
to consume. It’s wrapped with Mangum and deployed as an AWS Lambda function behind API Gateway — there’s
no long-running server to patch or scale.
Endpoints
Section titled “Endpoints”GET /members?state={state}&district={district}
Returns senators for the given state, and — if district is provided — the House representative for
that district too.
{ "senators": [ { "first_name": "Jane", "last_name": "Example", "role": "Senator", "party": "Democratic", "phone": "…", "website": "…", "photo_url": "…" } ], "representatives": [ { "...": "..." } ]}GET /version — returns the deployed version string, baked into the Lambda package at deploy time.
Useful for confirming a deploy actually landed without digging through CloudWatch.
Error handling: RFC 9457, not ad hoc JSON
Section titled “Error handling: RFC 9457, not ad hoc JSON”Errors are returned as application/problem+json, per RFC 9457
(“Problem Details for HTTP APIs”), instead of a bespoke { "error": "..." } shape. That means every error
response is self-describing and machine-parseable in a standard way, not something a client has to guess
at from reading the source.
A real 404 vs. a genuine vacancy
Section titled “A real 404 vs. a genuine vacancy”This is the detail that’s easy to get wrong: not every “no representative found” is an error.
- Unknown state →
404. - District number that doesn’t exist for that state →
404, validated against real U.S. House apportionment data (apportionment.py) — so a typo’d district number fails clearly instead of silently returning nothing. - District that exists but is currently vacant →
200with an empty list. A vacancy is a true, valid state, not an error.
Collapsing these into one generic “not found” would make the API lie about which case actually happened.
Deployment
Section titled “Deployment”- Lambda + RDS Proxy. Lambda’s connection model (many short-lived invocations) doesn’t play well with
Postgres’s per-connection overhead, so
cd-apiconnects through RDS Proxy rather than directly to RDS. - Tag-triggered deploys. A
cd-api-v*tag triggerscd-api-deploy.yml, which builds a Lambda zip withuv, checks it against Lambda’s 50MB limit, and callsaws lambda update-function-code. - Keyless CI. The deploy workflow assumes an IAM role via GitHub OIDC — no static AWS credentials stored in GitHub. More on this in CI/CD & Automation.
