Contract-Driven UI: Treat the API Contract as Your Product
Every week a frontend team sits idle waiting for an API, you’re burning time and money. But the real damage is deeper. You’re building a feature based on assumptions, and the longer you wait to validate those assumptions, the more expensive it becomes to be wrong. Finding out you need a different data field after you’ve built the database, written the API, and populated it with data isn’t just a setback; it’s a waste.
This post is about a workflow that front-loads the risk. It’s a way to validate your most critical assumptions with the cheapest possible resource: a text file. By treating your API contract as the primary product, you can stop wasting time and start building with confidence.
This shift allows us to build a workflow where frontend and backend development can happen concurrently. By defining the contract first, both ends can be built at the same time, synchronized by a shared understanding. The stack I’ve found effective for this combines three tools: Storybook, OpenAPI, and MSW.
Step 1: Define the Contract with OpenAPI
Before any code is written, we create an OpenAPI specification file. This YAML or JSON file is the single source of truth for what the data will look like. It defines the endpoints, the request parameters, and the shape of the response bodies.
This is the most critical step because it creates a fast feedback loop. The contract is just a text file—the simplest thing to create and, more importantly, the simplest thing to change. By building the UI against this contract using mock data, we can validate the data shape directly in the browser. If the UI needs a new field or a different structure, we don’t need a migration. We just update the text file. By the time we’re ready to build the database schemas, the data shape is rock solid because it has already been validated by the very thing that will consume it.
Step 2: Build in Isolation with Storybook
With the data shapes defined, the frontend can get to work in Storybook. Storybook provides an isolated development environment for each UI component. It’s a clean workshop for UI craftsmanship.
To make this effective, we create two distinct stories for each component:
- The Mocked Story: This story uses MSW to serve data generated from the OpenAPI contract, often using a library like Faker to create realistic and varied examples. This is your primary story for fast, iterative development. You can build the entire UI, including loading and error states, without ever needing a live backend.
- The Integration Story: This story is configured to bypass the mocks and make a real network request to the development API. As the backend team builds out the endpoint, you can use this story to perform full integration testing, ensuring the live data matches the contract and the component behaves as expected in a real-world scenario.
This two-story approach gives you the best of both worlds: the speed of parallel development and the confidence of end-to-end integration.
Step 3: Bring it to Life with Mock Service Worker (MSW)
This is where the workflow clicks. A component in Storybook is just a shell until it has data. MSW solves this. We use a tool to generate a mock API server directly from our OpenAPI spec. MSW then intercepts any network requests from our component and serves it that realistic, spec-driven mock data.
The result is a component in Storybook that looks and feels real. It’s populated with the exact data structure it will eventually consume from the live API. You can build all the loading states, error handling, and UI variations you need, all with complete confidence.
The Fine Print: When This Goes Sideways
Look, this workflow is powerful, but it’s not magic. It’s a system, and every system has its weak points. I’ve seen this pattern work wonders, and I’ve also seen it create new, subtle problems. Here’s where to watch your step.
The Tooling Tax. Let’s be clear: if there’s an API involved, there should always be a contract. That part isn’t optional. The overhead question comes from the tooling you choose to enforce it. For a tiny component with one simple GET request, setting up a complex mock data generator with dozens of fakerjs variations might be overkill. The core loop—contract, mock story, integration story—is always the right move. Just be pragmatic about how much polish you put into the mock. Don’t let the tooling become the project.
The Sync Problem. The OpenAPI spec is your single source of truth. That’s great, but it’s only true if everyone treats it that way. The moment a backend engineer makes a “quick fix” and deploys an endpoint change without updating the spec, you’ve created a new, insidious bug. Your mocks will be green, your tests will pass, and your production app will break silently. This workflow requires ruthless discipline from the entire team. It’s a social contract as much as a technical one.
The ‘Perfect’ Mock Problem. Data generated by fakerjs is clean and predictable. Real-world user data is a chaotic mess of nulls, unexpected characters, and legacy formats. Your mocked story will never show you that a user’s bio with an emoji will break your layout. That’s what the integration story is for—it’s your safety net. But don’t get complacent. The mocks give you speed, but they can give you a false sense of security. Trust, but verify.
The Organizational Impact
This isn’t just about making developers’ lives easier. This is about changing the economics of how you build software.
Accelerated Delivery. You’re no longer waiting in line. By enabling parallel work, you cut your feature lead time. The UI is being built at the same time as the backend logic. It’s simple math.
Reduced Rework. The most expensive changes are the ones you make to the data layer. By validating the contract with the UI first, you’re stress-testing your assumptions before you ever write a database migration. You eliminate the soul-crushing work of retro-fitting schemas and re-writing APIs.
De-Risked Features. You stop building features in the dark. The moment you see a component rendered with mock data, you know if the contract is right. You get that feedback instantly, not two weeks into a sprint when the backend is finally “done.” This isn’t about agile ceremonies; it’s about shortening the distance between an idea and the reality of whether it actually works.