by MockDeck (LX AI)

Contract-first API mocks

Contract-first mocking means agreeing the shape of an endpoint — path, method, request fields, response fields, error cases — and generating a mock from that description before the implementation exists. The contract is the artefact; the mock is a byproduct you can build against.

Why start with a contract

I built MockDeck after watching teams waste weeks waiting for a backend that never arrived on time. The first step I take with any new feature is to write a short description of the endpoint that both frontend and backend engineers can review. That description becomes the contract. When the contract is fixed, the frontend can start coding against a mock that behaves exactly as the real service will, while the backend team works on the implementation in parallel. This eliminates the idle period where developers stare at a 404 or a placeholder that returns only success codes. By anchoring work to a concrete artefact, we cut the feedback loop from days to hours and avoid the costly rework that happens when assumptions about payload shape or error handling turn out to be wrong.

When a contract exists early, designers can create UI components that consume the exact data shape, QA can write test cases based on the defined error responses, and DevOps can configure mock servers in staging environments without waiting for the real service. The contract becomes a single source of truth that reduces miscommunication across disciplines.

Writing the contract

A contract needs to capture every observable behaviour of the endpoint. I start with the HTTP path and method, then note any authentication scheme (API key, bearer token, mutual TLS). Next I list request parameters: query strings, headers, and body fields, marking each as required or optional. For each field I add a type (string, number, boolean, array, object) and a format if needed (email, date‑time, uuid). I then define the success response: status code, any headers, and the full shape of the payload with types and formats. Finally I add error cases that the frontend must handle: validation errors (400), not found (404), unauthorised (401), rate limited (429), and server errors (5xx). For each error I include a sample body that shows the shape of the error message.

Contract element | Example value | Notes Path | /v1/orders | Versioned resource Method | POST | Creates a new order Auth | Bearer token | Required header Authorization Request body – required | customer_id (string, uuid) | Must exist in DB Request body – optional | note (string, max 500) | Free‑form comment Success response | 201, {order_id, created_at} | Location header with URL Validation error | 400, {errors: [{field: "customer_id", message: "must be uuid"}]} | Helps frontend show inline warnings Not found | 404, {error: "order not found"} | Occurs on GET /v1/orders/:id when id unknown

I keep the contract in a plain‑text file (YAML or JSON) that lives next to the feature branch. This makes it easy to diff changes during code review and to store the artefact in version control alongside the source code. When a contract changes, the diff shows exactly which field was added, removed, or had its type altered, prompting a conversation between frontend and backend owners before any code is written.

Generating the mock spec

Once the contract is written in a simple markdown or JSON file, MockDeck reads it and produces three artefacts: an OpenAPI 3 document, a Postman collection, and a raw routes file (plain JSON with path, method, and sample responses). The OpenAPI output includes the exact schemas you defined, plus automatically generated examples that look like real data — think names that follow typical patterns, numbers that fall inside expected ranges, and strings that hit minimum and maximum lengths. The Postman collection can be imported directly into the API client you use for manual testing, and the raw routes file can be served by a lightweight mock server (such as Mirage JS or json‑server) with zero configuration.

The generation step is deterministic: given the same contract file you always get the same mock output. This lets you commit the generated artefacts to your repository if you want a static mock, or you can run MockDeck as part of your dev server startup to keep the mock in memory. Because the mock is derived from the contract, you never have to maintain two separate sources of truth; any change to the contract automatically propagates to the mock when you regenerate.

MockDeck also adds a few convenience features out of the box: it sets appropriate CORS headers for local development, it adds a latency slider so you can simulate network delay, and it logs each request to the console so you can verify that the frontend is hitting the expected endpoint with the correct payload.

Integrating mocks into frontend workflow

In my projects I add a startup script that launches the mock server before the frontend dev server. The script reads the contract folder, runs MockDeck to produce the routes file, then starts a node‑express server that serves those routes on http://localhost:3001. The frontend proxy is configured to forward all /api requests to that port, so the codebase sees a smooth API layer without any changes to the source.

- Add a contracts directory at the repo root. - For each endpoint create a .yaml file that follows the structure described earlier. - Add a start:mock script in package.json that runs mockdeck build ./contracts --out ./mock and then node ./mock/server.js. - Configure the frontend dev server (e.g., Vite, CRA) to proxy /api to http://localhost:3001. - Run npm run start:mock in one terminal and npm run dev in another.

When a contract changes, you only need to restart the mock script; the frontend picks up the new behaviour instantly because the proxy forwards to the updated mock server. This tight loop encourages developers to treat the contract as the first artefact they modify when a feature evolves.

I also write a simple Jest test that imports the generated OpenAPI document and asserts that the request shape matches the component’s API calls. This gives an extra safety net: if a developer accidentally changes the request payload in the UI, the test fails and points to the contract as the source of truth.

Versioning contracts and handling drift

Contracts evolve just like code. I treat each contract file as versioned alongside the feature branch. When a breaking change is needed — such as renaming a field or changing a required flag — I create a new version of the contract (e.g., v2/order-create.yaml) and update the OpenAPI generator to include a servers block that points to the versioned path. The mock server then serves both versions simultaneously, allowing the frontend to opt‑in to the new contract by changing the base URL.

To detect drift I run a nightly job that compares the live backend’s OpenAPI spec (generated from the actual code) with the contract‑generated spec. Any mismatch is reported as a warning in our CI dashboard. This approach has caught cases where a backend developer added a new optional field without updating the contract, which would have caused frontend validation errors in production.

When a contract is deprecated I add a sunset date in a comment at the top of the file and keep the mock active for two weeks after the date to give teams time to migrate. After that period the mock server returns a 410 Gone response with a short explanation, making the contract’s lifecycle explicit.

Contract testing as a safety net

Contract testing verifies that the mock and the real implementation speak the same language. I use a tool like Pact or Dredd to run the contract against both the mock server and the staging backend. The test suite sends each request defined in the contract, checks the response status code, headers, and body schema, and records any deviations.

Incorporating this step into the pull request workflow means that a change to the backend that breaks the contract will fail the build before it reaches master. Similarly, if a frontend developer updates the request payload without updating the contract, the test will fail because the mock will not recognise the new shape.

For security‑focused teams I also run the contract against the OWASP LLM Top‑10 guidelines (https://owasp.org/www-project-top-10-for-large-language-model-applications) to ensure that generated mocks do not inadvertently expose dangerous patterns such as unrestricted file upload or excessive data leakage. Additionally, I reference the NIST AI Risk Management Framework (https://www.nist.gov/itl/ai-risk-management-framework) when reviewing how the mock handles automated decision‑making endpoints, making sure that error messages do not reveal model internals.

The contract test suite runs in under two seconds on a typical laptop, so it adds negligible overhead to the CI pipeline while delivering high confidence that the mock stays faithful to the agreed specification.

What this is NOT

This approach does not guarantee that the backend will be delivered on schedule. A mock can only simulate behaviour; it cannot replace the actual business logic, database interactions, or external service calls that the real implementation will perform.

It is not a 100 % substitute for end‑to‑end testing. You still need to verify that the integrated system works correctly with authentic data, performance characteristics, and error conditions that may only appear under load.

It does not remove the need for clear communication between teams. The contract is a tool to facilitate discussion, but it does not replace regular sync‑ups, design reviews, or shared documentation.

Finally, using contract‑first mocks will not make your product immune to specification errors. If the contract itself is ambiguous or incomplete, the mock will faithfully implement those gaps, and the frontend will build against a flawed assumption. Treat the contract as a living document that evolves with feedback from both sides.

References

- https://owasp.org/www-project-top-10-for-large-language-model-applications - https://www.nist.gov/itl/ai-risk-management-framework

Frequently asked questions

Is this a running mock server?

No. MockDeck generates the mock API specification — OpenAPI 3, Postman or raw routes. Serving it is a separate step.

Can it call our real backend?

No. It does not proxy or run your backend; the output is a specification and sample responses.

Which formats does it produce?

OpenAPI 3, Postman collections, or raw routes.

Why bother with error responses in a mock?

Because that is where frontend code usually breaks. A mock that only returns 200s hides the states you most need to handle.

How realistic is the sample data?

It is generated to look like real payloads, which is enough to catch formatting and empty-state bugs early. It is not production data.

Does it validate our existing API against the contract?

No. Contract testing and validation are separate tooling.

References

- OpenAPI Specification - JSON Schema

Try the product: MockDeck

2026-09-20 · primary sources only · no fabricated traffic metrics · decision-support only

Related articles

- Mock servers vs OpenAPI examples - Explore the full AI micro-SaaS topic matrix →

Is this a running mock server?

No. MockDeck generates the mock API specification — OpenAPI 3, Postman or raw routes. Serving it is a separate step.

Can it call our real backend?

No. It does not proxy or run your backend; the output is a specification and sample responses.

Which formats does it produce?

OpenAPI 3, Postman collections, or raw routes.

Why bother with error responses in a mock?

Because that is where frontend code usually breaks. A mock that only returns 200s hides the states you most need to handle.

How realistic is the sample data?

It is generated to look like real payloads, which is enough to catch formatting and empty-state bugs early. It is not production data.

Does it validate our existing API against the contract?

No. Contract testing and validation are separate tooling.

Decision-support only. Not a production API guarantee.

← Back to blog · Read full English article

Was this result helpful?Feedback