It started the way most of my projects start — I wanted one simple thing. I wanted to pull pitcher stats from the MLB StatsAPI and display them in a React app. That's it. A fetch call, some JSON parsing, a nice little card component. Should've taken an afternoon.
Six weeks later I had a 1,500-line TypeScript wrapper with 102 async methods, TTL caching, hydration profiles, and a namespaced architecture that I'm genuinely proud of. But if I'm being honest, I made some choices early on that cost me time later. So here's the retrospective.
The namespace thing was right. I organized methods under logical groups — players.getStats(), teams.getRoster(), game.getPitchByPitch(). This made the API self-documenting. When I came back to the code after a two-week break, I didn't have to grep through a flat list of 100+ functions. The namespacing paid for itself immediately.
TTL caching saved me from myself. I was hammering the MLB API during development. Every hot reload was firing off requests. I added a simple TTL cache — roster data gets 24 hours, live game data gets 30 seconds, historical stats get a week. My request volume dropped by probably 80%. But here's the thing I'd change: I'd build the cache layer first next time, not bolt it on after I'd already structured 60 methods without it.
Hydration profiles were the real unlock. The MLB API returns a lot of nested data. A single player endpoint can come back with stats, transactions, draft info, education — the whole biography. Most of the time you don't need all of it. So I built hydration profiles: 'minimal', 'standard', 'full'. Each one tells the wrapper which fields to request and how deep to go. This made the frontend way snappier because I wasn't shipping 40KB JSON blobs for a name and an ERA.
What I'd do differently: I'd start with a schema. I dove straight into writing methods based on what the API endpoints looked like, which meant my TypeScript interfaces evolved organically — which is a nice way of saying they were messy for a while. If I'd mapped out my data model first, the types would've been cleaner from day one. I'd also write integration tests earlier. I had unit tests, but the first time I caught a breaking API change was in production. Not great.
The bigger lesson is about scope. When you're building something for yourself, there's no PM telling you to stop. The feature list just keeps growing. At some point I had to decide: is this a wrapper or is this an SDK? I chose wrapper, drew the line, and shipped. That discipline was harder than any of the code.