Migrated the HTTP API from Fiber to Huma v2 — 649 paths and 760 operations — reaching and holding 100% parity between the routes the server registers and the OpenAPI description it publishes.
Work carried out: 2025
Situation. The API started life on Fiber, with request validation written out by hand, endpoint by endpoint. That’s fine when there are a handful of endpoints. It stops being fine as the surface grows: the hand‑rolled validation turns into a maintenance tax, and small inconsistencies creep in because every endpoint’s checks are their own little snowflake. And there was no single description of the API’s shape anywhere.
Task. The goal was validation coming from the types instead of from hand‑written checks, and an actual contract describing the API — without stopping to do a big‑bang rewrite.
Action. The HTTP layer moved onto Huma v2, sitting on top of Fiber, so the existing runtime stayed. Each endpoint gets input and output structs, and Huma generates the request validation and response modelling from those types. An OpenAPI description comes out of it for free, which means the documentation tracks the code instead of rotting in a wiki. Everything new got written against Huma and the existing routes migrated over, with exactly two endpoints left on raw Fiber — the WebSocket ones, where you genuinely want the socket and Huma’s request/response model doesn’t fit. What that has grown into is 649 paths carrying 760 operations, and a check in CI that compares the routes the server actually registers against the ones the OpenAPI description advertises. Parity is 100%, and it stays there because a route that isn’t described fails the build.
Result. New endpoints get validation and current documentation without anyone doing extra work for it, and a whole class of request‑handling bugs — the “oh, we forgot to check that field here” kind — went away. At 760 operations the description is the only practical way anyone reads the API, so the guarantee that it is complete matters more than it did at fifty. The typed contract made the API both safer to change and easier to hand to someone else, because the types tell you what an endpoint expects.