Case study
GoPoli
Ride-sharing web app for the students of a public university in Medellín
Drivers publish trips with their route and seats, passengers join them, and every trip has its own chat. I took it from a Flutter app in one repository to a web app, an API and a database image, each tested and deployed on its own.
The problem
Students of Politécnico Colombiano Jaime Isaza Cadavid needed a way to offer and join shared rides to campus. GoPoli started as a class project, with a Flutter app and its backend in a single repository.
Constraints
- Four contributors working part-time.
- One server for the web app, the API and the database.
- Maps and routes without paid API keys.
What I built
- Split the original repository into one per component, rewriting history so each keeps its own commits, and scanned the whole history for leaked secrets.
- A PostgreSQL 16 image that owns the schema, with numbered init scripts and optional demo data. The API validates against it and refuses to start on a mismatch.
- A Spring Boot 4 API with JWT and BCrypt, with business rules in small policy classes that are easy to test.
- A Next.js 16 PWA with Leaflet maps and OSRM routes that fall back to a straight line when routing fails. The JWT lives only in memory.
- The API address is injected when the container starts, so one image serves every environment.
- Each image is published to GHCR with an SBOM and provenance, and one central workflow deploys the whole app over SSH and checks /health from outside.
Architecture
How a request travels
The web app talks to the API through Nginx; the database is reachable only on the internal network.
Browser
- Next.js 16 PWA
- Leaflet with OpenStreetMap
- OSRM routes
Server
- Nginx with TLS
Services
- Spring Boot 4 API with JWT and BCrypt
- PostgreSQL 16, owner of the schema
Delivery
- GHCR images with SBOM and provenance
- One workflow deploys the whole app over SSH
- Public /health check after each deploy
Decisions and trade-offs
The database image owns the schema
When JPA creates the tables, the schema lives in no file anyone can review.
The token only in memory
A token kept in the browser's storage is within reach of the page's scripts.
OpenStreetMap and public OSRM
Maps and routes could not depend on paid API keys.
Results
- 41 tests in the API and 18 in the web app run in CI, along with CodeQL and dependency review.
- A 31-step API smoke test, from sign-up to trip history, passes in development and in the production setup.
- Production checks without signing in, which cover TLS, the redirect to HTTPS, /health, the 401 without a token and a CORS policy that accepts only the web app.
Open items
- Ratings between riders. The first app had them and the API does not support them yet, so the web app leaves them out.
Stack
Pick a technology to see the projects that use it.





