— Project 02 · Case study

QuietMetrics

My GitHub stats were scattered across tabs and graphs I never trusted. QuietMetrics is the one page I actually wanted.

React TypeScript Node / Express PostgreSQL Prisma Socket.IO Live
quietmetrics.vercel.app
QuietMetrics login — GitHub OAuth, minimal interface

GitHub stats without the noise.

GitHub's own interface is optimised for activity and contribution streaks: things that look good on a profile but don't really tell you much about what you've built, or how your work is spread across languages and projects.

I wanted a clean dashboard: connect your account, see your repository stats, language breakdown, and recent activity, all presented with restraint. There's no gamification, no engagement tricks, just the numbers I actually care about.

The auth side ended up being more involved than I expected. GitHub OAuth handles the login, but then you need to exchange the code for an access token, store it securely, and handle expiry gracefully on every request after that. Getting that part right (storing the token in an httpOnly cookie instead of localStorage) was a small decision that turned out to matter a lot for security.

The things that surprised me.

01

The OAuth happy path is the easy 10%

Logging in with GitHub is the simple part. Refreshing an expired token without booting the user out, handling a revoked permission gracefully, recovering from a bad redirect: that's the actual work, and it took several rewrites before the flow stopped feeling fragile.

02

Cutting features was harder than shipping them

A dashboard can show anything you can pull from an API, that's never the hard part. The actual work is deciding what's worth a recruiter's attention. I shipped three features and then pulled all three once I noticed they made the page busier without making it clearer.

03

Cookies over localStorage, no contest

Storing the GitHub token in an httpOnly cookie means client-side JavaScript can never read it, so a whole category of XSS attack just doesn't apply here. It's a small decision on paper. It's also the kind of thing that's invisible when you do it right and obvious when you don't.

04

Real-time took an afternoon, auth took a week

I expected real-time updates to be the hard part of this build. At this scale, Socket.IO is really just emit and listen: the server pushes a stats update, the client renders it, done. It ended up being one of the faster pieces, while the auth flow ate most of the week.

What it's built on.

React + TypeScript

Frontend SPA. TypeScript kept the GitHub API response shapes honest across the whole data flow from fetch to render.

Node / Express

REST API backend. Handles the GitHub OAuth exchange, token storage, and proxies GitHub API requests so the access token never touches the client.

PostgreSQL + Prisma

Stores user accounts and persists GitHub tokens. Prisma's type-safe query builder made schema changes painless and kept the data layer readable.

Socket.IO

Pushes live stat updates to connected clients. When GitHub data refreshes on the server, every open dashboard updates without the client needing to poll.

JWT + httpOnly cookies

Session tokens are stored in httpOnly, SameSite cookies. No token exposure to JavaScript, no localStorage risk.

Vercel

Frontend deployed to Vercel. Zero-config deploys on push to main kept the feedback loop tight while building.