Static-first personal site with supporting APIs and infrastructure:
services/websiteservices/contact-apiservices/salary-inflation-checker-apiservices/trading212-apiplatform/trading212infra
craig-watt-website/
├─ services/
│ ├─ website/
│ ├─ website-e2e/
│ ├─ contact-api/
│ ├─ salary-inflation-checker-api/
│ └─ trading212-api/
├─ platform/
│ └─ trading212/
└─ infra/
├─ bootstrap/
├─ modules/
└─ services/
pnpm install
pnpm dev
pnpm build
pnpm start
pnpm lint
pnpm test
pnpm e2e
pnpm storybook
pnpm build-storybook
pnpm build:functionspnpm build produces the exported static site in services/website/out, and pnpm build:functions bundles the Lambda handlers into dist/services/*.
Dynamic data is fetched by AWS Lambda and proxied through API Gateway, so the site stays static at the edge while the data updates independently.
GET /api/trading212for portfolio dataPOST /api/contactfor the contact formGET /api/salary-inflation-checkerfor salary inflation checker snapshots
The salary inflation checker endpoint uses public sources only, so there are no extra API keys to store in GitHub Secrets for the first pass:
- ONS Consumer Price Inflation bulletin for CPIH and the inflation baseline
- ONS ASHE Table 7 dataset page for salary-by-location source acquisition
- Tesco lunch meal-deals page for current meal-deal price snapshots
The production stack is now:
- S3 for static asset storage
- CloudFront for CDN +
/api/*routing - API Gateway for public API ingress
- Lambda for
contact,salary-inflation-checker, andtrading212 - Route 53 for DNS
Terraform entrypoint:
cd infra
terraform init
terraform applyFor the contact form, set:
contact_email_fromto a sender on the verified domain, for exampleno-reply@craigwatt.co.ukcontact_email_toto one recipient or a comma-separated list, for examplecraig-watt@live.co.uk,craig@webrefine.co.uk
Terraform also requests SES email verification for every destination address in contact_email_to. If the SES account is still in the sandbox, each recipient must click the AWS verification email before the contact form can deliver there.
Terraform state locking now uses S3 lockfiles, so there is no separate DynamoDB lock table.
Bootstrap resources for the GitHub Actions deployment role live under infra/bootstrap.