How a Clean openapi.json Saved My Sanity as a Frontend Developer
I used to think keeping the frontend and backend in sync was just part of the job — like merge conflicts or forgotten console logs. But one day, while switching between my two projects (one for the backend, one for the frontend), I realized something: most of my bugs weren’t logical errors — they were communication errors.
The Pain of Two Worlds
My setup was pretty standard. The backend lived in its own folder, written in NestJS with beautiful DTOs, validation, and Swagger docs. The frontend sat separately — a Next.js project that fetched data from those endpoints. All was good… until it wasn’t. Every time the backend team (which was usually just me wearing a different hat) changed a DTO — added a new field, renamed a key, changed the response type — the frontend started throwing mysterious errors. It was like playing whack-a-mole:
- One endpoint suddenly returned
user_nameinstead ofusername. - A nested field moved from
data.detailstodata.info.details. - The response for
GET /exercise/:idchanged shape after a refactor. And the worst part? The frontend had no idea these changes happened. I’d have to open Swagger, manually inspect the response, update my TypeScript types, fix my hooks, and hope I didn’t miss anything. That’s when I realized something important:
The problem wasn’t in the code — it was in the contract between the backend and frontend.
The “Aha” Moment
One night, after another “why-is-this-type-undefined” debugging session, I opened the openapi.json that NestJS generated automatically.
It was beautiful.
Every route, every DTO, every schema — perfectly described in one file.
That’s when it hit me:
Why am I manually updating types when the backend already knows everything? The backend literally describes itself in this
openapi.json. All I needed to do was make the frontend listen.
Building the Bridge
So I built a small CLI.
At first, it was just a script that parsed openapi.json and generated TypeScript types.
Then I added API functions.
Then React Query hooks.
And before I knew it, it became a full-blown frontend service generator.
The CLI reads the backend’s OpenAPI schema and outputs something like this:
services-generated/ exercise/ exercise.ts index.ts types.d.ts use-exercise.ts
Each folder represents an API resource, like exercise or user.
Each file has a specific role:
exercise.ts→ API calls (e.g.getExercise,createExercise)types.d.ts→ auto-generated TypeScript interfaces from DTOsuse-exercise.ts→ ready-to-use React hooks powered by React Queryindex.ts→ exports everything neatly No manual work. No mismatched types. Just run the CLI, and everything updates automatically.
The Magic of a Clean OpenAPI
Here’s where things got interesting.
The whole system relied on one thing: a clean and well-structured openapi.json.
If your OpenAPI file is messy — with missing schemas, loose any types, or inconsistent naming — the generator falls apart.
So I started treating openapi.json as the single source of truth.
Not as “just documentation,” but as the contract that defines the language between backend and frontend.
And when that contract is clean, everything else becomes effortless:
- Type safety comes for free.
- Frontend code updates in seconds after backend changes.
- New developers can explore the generated files and instantly understand the API.
- Bugs caused by API mismatches simply vanish.
The Result
Now, when I make changes on the backend, all I do is:
-
Regenerate
openapi.json(NestJS does it automatically). -
Run:npx generate-services --openapi ./openapi.json --output ./src/services-generated
-
Done. The frontend instantly has the latest hooks, types, and service methods — perfectly in sync with the backend. No more guessing what a DTO looks like. No more broken contracts. No more 2 a.m. “why-is-this-undefined” moments.
The Lesson
We often think of OpenAPI as something we generate at the end — a nice-to-have documentation for external users.
But it’s so much more powerful when you flip that perspective.
A clean openapi.json is not the byproduct of your backend.
It’s the bridge between your backend and frontend — a living, auto-generated contract that can drive your entire development flow.
Once you start seeing it that way, everything changes.
Closing Thoughts
Today, every project I build — no matter how small — starts with a commitment to a clean OpenAPI structure. Because now I know:
The more consistent your API schema, the more freedom your frontend has to evolve. And maybe the best part? The next time I change a DTO on the backend, I don’t dread it. I just smile, regenerate, and keep building.