Screenshot of the Mis Centros Educativos school map and filters

Mis Centros Educativos

  • Next.js
  • React
  • TypeScript
  • PostgreSQL
  • Prisma
  • Supabase
  • Docker

About this project

Every year, tens of thousands of public-school teachers in the Community of Madrid compete for permanent postings in a process known as el concursillo — a transfer contest where applicants rank schools by preference, often with nothing more than a spreadsheet and a paper list to go on. Mis Centros Educativos turns that into a searchable, filterable map: every school in Madrid, filterable by territorial zone (DAT), type (CEIP, IES, FP, EOI…), ownership, and special attributes like bilingual programs or hard-to-staff status — plus a real commute-time calculation from the teacher’s own address, by car, bike, on foot, or public transit.

It’s a free, public tool I designed, built, and operate solo — product, application code, and infrastructure. It currently covers more than 3,500 schools and has been used by over 100 teachers preparing their own concursillo.

Key features

Technical highlights

Stack: Next.js 16 (App Router) + React 19 + TypeScript strict, Tailwind CSS 4, Prisma 7 over PostgreSQL (Supabase), Supabase Auth (magic link), Vitest + Testing Library + Playwright for e2e.

Self-hosted routing. Rather than pay per-request for a commercial directions API, travel times are computed by self-hosted OpenRouteService and OpenTripPlanner 2 containers, built from real OpenStreetMap and CRTM transit data for the Madrid region. Geocoding runs through Photon.

Operational maturity. A structured JSON logger, a debounced email alerting system to avoid noisy false positives, and a /api/health endpoint that checks both database connectivity and TLS certificate expiry, with DNS-01 ACME issuance through Cloudflare.

What I learned

Running this alone — from provisioning the VPS to writing the FAQ copy — let me put the full range of what I know to work on one project: architecture, backend, infrastructure, and product decisions, all under one roof. The clearest lesson was around self-hosting versus a SaaS API for the routing engine — owning OpenRouteService and OpenTripPlanner myself instead of paying per request gave me control over cost and data, in exchange for owning the operational complexity. Knowing when that trade-off is worth it is something I’d now weigh with much more confidence on the next project.