DEV Community

Cover image for API Contracts Break Silently. Here's How to Catch Them the Moment They Do.
Maesi
Maesi

Posted on

API Contracts Break Silently. Here's How to Catch Them the Moment They Do.

A backend developer renames a field. The tests pass. It ships. Three days later the mobile app is crashing and nobody knows why.

This is how API contract drift works. It's not a discipline problem. It's a tooling problem. When nothing enforces the contract at runtime, you're relying on everyone to manually keep specs, code, and clients in sync across multiple teams and deploy cycles. That doesn't scale.

I built a validation layer into Mokapi to fix this. The idea is simple: Mokapi sits between your client and backend, validates every request and response against your OpenAPI spec, and gives you an immediate, actionable error the moment something violates the contract.


The Forwarding Script

One JavaScript file is all it takes:

import { on } from 'mokapi'
import { fetch } from 'mokapi/http'

export default async function() {
  on('http', async (request, response) => {
    const url = getForwardUrl(request)

    if (!url) {
      response.statusCode = 500
      response.body = 'Failed to forward request: unknown backend'
      return
    }

    try {
      const res = await fetch(url, {
        method: request.method,
        body: request.body,
        headers: request.header,
        timeout: '30s'
      })

      response.statusCode = res.statusCode
      response.headers = res.headers

      const contentType = res.headers['Content-Type']?.[0] || ''
      if (contentType.includes('application/json')) {
        response.data = res.json()  // triggers response validation
      } else {
        response.body = res.body
      }
    } catch (e) {
      response.statusCode = 500
      response.body = e.toString()
    }
  })

  function getForwardUrl(request) {
    switch (request.api) {
      case 'backend-1':
        return `https://backend1.example.com${request.url.path}?${request.url.query}`
      case 'backend-2':
        return `https://backend2.example.com${request.url.path}?${request.url.query}`
      default:
        return undefined
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

request.api contains the info.title from your OpenAPI spec and is used here purely for routing to the right backend URL.
The choice between response.data and response.body is what controls validation: setting response.data tells Mokapi to validate the response against the spec schema, while setting response.body skips validation entirely. So for JSON responses you want validated, use response.data. For anything else, response.body passes it through as-is.

No changes to your backend. No changes to your client. Just point the client at Mokapi instead.


What It Actually Catches

More than just a renamed field. Mokapi validates:

  • Request method, URL structure, headers, query parameters
  • Request body: required fields, data types, format constraints
  • Response status codes, headers, and body structure

A backend that starts returning a string where the spec says number. A client sending a request body missing a required field. A response body that doesn't match the documented schema. All caught at the boundary, with a message that tells you exactly what violated what.


Where to Use It

It's the same script in every context. Between frontend and backend during local development. Between microservices in CI. In Playwright tests so your test suite becomes a contract test suite automatically. In Kubernetes preview environments before changes reach staging.


Full Walkthrough

The complete guide on mokapi.io covers the full setup.

👉 Read the full guide here

Happy to answer questions in the comments.

Top comments (0)