Building a Community-Driven Transit API with Spring Boot
How I built a REST API for Philippine transit routes and fares to learn Spring Boot, JPA, and role-based access control.
The Problem
Philippine transit data is fragmented — routes, fares, and stops live in Facebook groups, forum posts, and PDFs. There's no single machine-readable source, and no easy way for commuters or developers to query it.
I wanted to build something structured: a REST API where routes, stops, and fares are properly modeled, versioned, and open for community contribution — and use the project as a way to actually learn Spring Boot instead of just reading about it.
Modeling the Domain
The core entities are straightforward relationally: a Route has many Stops in sequence, and each pair of stops on a route has an associated Fare. Operators own and manage their own routes; commuters can search, save routes, and submit reports (like outdated fares or incorrect stops) for admins to review.
@Entity
public class Route {
@Id @GeneratedValue
private Long id;
private String routeCode;
private String vehicleType;
@OneToMany(mappedBy = "route", cascade = CascadeType.ALL)
private List<Stop> stops;
}
Getting the relationships right — routes to stops, stops to fares, operators to routes — was most of the actual design work. Real transit data doesn't always fit clean models: routes get renumbered, fares change by time of day, and stop ordering isn't always consistent across data sources.
The API
Built on Spring Boot 3.5 with Spring Data JPA and PostgreSQL, the API exposes public endpoints for browsing routes, stops, and fares, plus authenticated endpoints for operators to manage their own routes and admins to verify operators and moderate reports.
Auth is handled with Spring Security + JWT, with three roles:
COMMUTER— save routes, submit reportsOPERATOR— manage their own routes, stops, and faresADMIN— verify operators, review reports, view analytics
GET /routes/search?origin=&destination=
GET /fares/calculate?from=&to=
POST /routes [OPERATOR]
PATCH /operators/{id}/verify [ADMIN]
Getting role-based access right — making sure an operator can only edit their own routes, for example — was one of the more useful parts of the project for actually understanding how Spring Security handles authorization beyond just authentication.
What I Learned
This was as much a Spring Boot learning project as it was a transit-data project. A few things that stuck:
- JPA relationships are easy to get wrong in subtle ways — cascade behavior, fetch types, and bidirectional mappings all have real consequences once you're past toy examples.
- Role-based access control is more than checking a role string — it's making sure ownership checks (operator manages their own routes) are enforced at the service layer, not just the route layer.
- Community-submitted data needs a review/approval flow from day one, or bad data becomes permanent fast.
There's still work to do — tests, Docker, and a proper deployment are next — but the core API models the domain in a way that could realistically support a real commuter-facing app on top of it.