Rate My Uni

Full-stack web app
Live site →

A web app that lets students post and read reviews of UK universities. I built it end-to-end: a React (Material UI) frontend backed by a Spring Boot REST API on Java 21, with PostgreSQL handling user-generated content and Cloudflare R2 storing uploaded images.

Production lives across two providers. Railway runs the API, the frontend, and Postgres on its private network. Cloudflare fronts the public hostnames for DNS, TLS, and CDN, and serves images from an R2 bucket through a custom domain (rate-my-uni-images.co.uk). Auth covers JWT, Google OAuth, and email magic links; transactional email goes out through Resend on top of Amazon SES, with mailboxes hosted on Zoho. The backend uses EhCache for in-process caching, which is fine for a single replica but would need swapping for something shared (Redis) before scaling out.

The interesting parts were stitching together the multi-provider setup without it turning into a mess, modelling reviews and ratings cleanly in SQL, and getting comfortable with deploying both halves of the stack to a live domain.

React Spring Boot Java 21 PostgreSQL Cloudflare R2 Railway JWT / OAuth

This portfolio site

Cloud Resume Challenge on GCP
GitHub →

A take on the Cloud Resume Challenge built entirely on Google Cloud. The site itself is static HTML/CSS in a GCS bucket, served through an HTTPS load balancer with Cloud CDN in front. A small Python Cloud Run function increments a Firestore counter every time the page loads.

The fun part is the plumbing. All of the GCP resources (bucket, Cloud Run service, Firestore, load balancer, SSL cert, URL maps) are defined in Terraform with state in GCS, and changes go through a GitHub Actions pipeline that runs plan on every PR and applies on merge to main. The frontend and backend each have their own Cloud Build triggers that ship on push. No service account keys anywhere, just Workload Identity Federation.

GCP Terraform Cloud Run Firestore Python GitHub Actions Cloud Build