· 3 min read
Building Type-Safe APIs with OpenAPI
When I joined Stream, one of the first things I noticed was the friction between frontend and backend. Every API change meant manual type updates, and integration bugs would slip through despite the team’s best efforts.
I pushed to go all-in on OpenAPI. The idea was simple: the API spec becomes the single source of truth, and everything else (types, clients, validation) is generated from it.
The Problem
The dashboard consumed many API endpoints. Each one had hand-written TypeScript types that drifted from the actual API response over time. Engineers would discover mismatches in production, leading to runtime errors that could have been caught at build time.
The typical workflow looked like this: backend ships a change, updates the OpenAPI spec, then notifies the frontend team to update their types. Sometimes the notification didn’t happen. Sometimes the types were updated but slightly wrong. The result was a constant trickle of integration bugs.
The Approach
I built a codegen pipeline that runs on every API spec change. It generates TypeScript types, API client functions, and request/response validators. The frontend never defines its own API types because they come from the spec.
The pipeline hooks into CI. When the OpenAPI spec changes, the CI workflow regenerates the types and opens a PR. The frontend team reviews the diff, which shows exactly what changed in the API contract. No more guessing.
I used a combination of openapi-typescript for type generation and custom templates for the client functions. The generated client is thin, wrapping fetch with proper typing, error handling, and request validation.
What Changed
The impact was immediate. Integration errors dropped to near zero. Code reviews became simpler because reviewers could focus on logic rather than checking whether types matched the API.
The biggest win was confidence. Engineers could refactor API consumption code knowing the compiler would catch any contract violations. What used to require manual verification now happened automatically at build time.
Lessons Learned
Codegen is not a silver bullet. The generated code needs to be readable, because if engineers can’t understand the output, they’ll work around it. I spent time tuning the templates until the generated client felt hand-written.
The other lesson: treat the OpenAPI spec as a first-class artifact. If the spec is wrong or incomplete, the generated code will be too. I added spec validation to the CI pipeline so malformed specs fail before they reach consumers.