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.

  1. Browser

    • Next.js 16 PWA
    • Leaflet with OpenStreetMap
    • OSRM routes
  2. Server

    • Nginx with TLS
  3. 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

  1. The database image owns the schema

    When JPA creates the tables, the schema lives in no file anyone can review.

  2. The token only in memory

    A token kept in the browser's storage is within reach of the page's scripts.

  3. 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.