CI/CD Pipeline Dashboard — GitHub Actions Health Visualizer
Real-time CI/CD health board for any GitHub user — Next.js 14 frontend, optional Azure Functions backend for heavy aggregation, no token required for public repos.
Demo · Live recording
Problem
The GitHub Actions UI is per-repository: to compare CI/CD health across a portfolio of repos you click through each one, eyeball the badges and try to remember what 'normal' looks like. I wanted a single screen that answers 'which of my pipelines are healthy right now?' with comparable signals — last run status, success rate over the last 30 runs and average build duration — refreshing on its own. The product had to work for any GitHub user with no setup beyond typing a username.
Computing aggregated stats over 30+ public repos in one HTTP request is exactly the fan-out shape that pushes a Vercel route handler against its time budget — multiple list-workflow-runs calls per repo, each paginated, each subject to GitHub's per-IP and per-token rate limits. I also wanted the deploy to work without any GitHub credentials (public repos, 60 req/h ceiling) but to scale gracefully when a token is added (5,000 req/h), and to be honest about what runs where instead of hiding the slow path behind a black-box edge function.
Solution
Next.js 14 App Router on Vercel renders the dashboard, charts and detail pages, and exposes four API routes that talk to the GitHub REST API directly. The aggregation logic is a pure TypeScript module shared between the Next.js handler and an optional Azure Function on the Consumption Plan; a single USE_AZURE_BACKEND environment flag flips between 'compute on Vercel' and 'proxy to Azure'. When the Azure path is on, the heavy fan-out runs inside a 10-minute Function timeout with Application Insights tracing every GitHub call, rate-limit warning and exception. The frontend stays a thin Recharts/Tailwind client that polls every 30 seconds.
Architecture
Browser → Next.js 14 App Router (Vercel) → GitHub REST API. Optional fork: Next.js handler → Azure Function (Consumption Plan, Node 20) → GitHub REST API, with Application Insights telemetry on the Function path.
Product
Key metrics
Live links
Key decisions
Token optional, never required
Public repos already work at 60 req/h, which is fine for a single-user demo. A no-scope PAT raises that to 5,000 req/h for production use. Making auth optional means the live demo stays usable for any visitor and keeps the privacy story honest — the app does not collect tokens it does not need.
Same aggregator on Vercel and Azure
The aggregation function is a pure TypeScript module, imported by both the Next.js route handler and the Azure Function. There is exactly one definition of 'success rate over the last 30 runs', so the two backends cannot drift in interpretation — only in execution time and rate-limit budget.
Backend toggle behind one env var
USE_AZURE_BACKEND is the only switch. Off → compute on Vercel, on with a URL → proxy to Azure, on without a URL → HTTP 500 with a clear message. No code change, no redeploy of the frontend, and a misconfiguration is loud instead of silently slow.
30-second polling, not WebSockets
GitHub Actions runs are minute-scale events, not millisecond-scale. A simple setInterval poll keeps the frontend stateless, plays well with serverless caching layers and avoids holding open connections through Vercel's edge.
Reflection
The interesting part was not the GitHub API plumbing — it was deciding where the heavy work runs and making that decision visible. One pure aggregator, two backends, one env flag: the Vercel route stays fast for normal use, and a user with 50 repos can flip the switch and get a 10-minute Azure budget without changing any frontend code. Once that boundary was clear, everything else (charts, polling, telemetry) was just plumbing on the right side of it.