Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8c5a411
fix column width for wider IP address
marle3003 Dec 3, 2025
de9b7ad
Merge remote-tracking branch 'origin/develop' into develop
marle3003 Dec 3, 2025
c76f7f5
Bump github.com/blevesearch/bleve/v2 from 2.5.5 to 2.5.6
dependabot[bot] Dec 5, 2025
9e5b4dd
Bump vite from 7.2.4 to 7.2.6 in /webui
dependabot[bot] Dec 5, 2025
bef3cb5
Merge pull request #764 from marle3003/dependabot/go_modules/develop/…
github-actions[bot] Dec 5, 2025
b5f4686
Bump github.com/evanw/esbuild from 0.27.0 to 0.27.1
dependabot[bot] Dec 5, 2025
fd1f0c4
Merge pull request #766 from marle3003/dependabot/go_modules/develop/…
github-actions[bot] Dec 5, 2025
b757f9f
update text
marle3003 Dec 5, 2025
f3a5425
Merge branch 'develop' into dependabot/npm_and_yarn/webui/develop/vit…
marle3003 Dec 5, 2025
63d4a9c
Merge pull request #768 from marle3003/dependabot/npm_and_yarn/webui/…
github-actions[bot] Dec 5, 2025
e6c7c5b
Bump ace-builds from 1.43.4 to 1.43.5 in /webui
dependabot[bot] Dec 5, 2025
de147b2
Merge pull request #765 from marle3003/dependabot/npm_and_yarn/webui/…
github-actions[bot] Dec 5, 2025
15e6a06
Bump prettier from 3.6.2 to 3.7.4 in /webui
dependabot[bot] Dec 5, 2025
bd6b951
Merge pull request #767 from marle3003/dependabot/npm_and_yarn/webui/…
github-actions[bot] Dec 5, 2025
dff7cc1
fix JavaScript shared in module mokapi
marle3003 Dec 5, 2025
e3b64ce
Merge remote-tracking branch 'origin/develop' into develop
marle3003 Dec 5, 2025
5ab392f
fix JavaScript shared in module mokapi
marle3003 Dec 5, 2025
ee80396
add new blog article
marle3003 Dec 7, 2025
2885628
Merge remote-tracking branch 'origin/develop' into develop
marle3003 Dec 7, 2025
2ab98be
update package-lock.json
marle3003 Dec 7, 2025
c1e68f9
update table font-size
marle3003 Dec 7, 2025
0200762
fix test
marle3003 Dec 7, 2025
920e97e
fix soft 404 for bots
marle3003 Dec 7, 2025
2fb6d94
update article
marle3003 Dec 7, 2025
cca38f2
Merge branch 'main' into develop
marle3003 Dec 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@
"description": "Explore Mokapi's resources including tutorials, examples, and blog articles. Learn to mock APIs, validate schemas, and streamline your development."
},
"items": {
"Ensuring API Contract Compliance with Mokapi": "resources/blogs/ensuring-api-contract-compliance-with-mokapi.md",
"Mock APIs based on OpenAPI and AsyncAPI": "resources/blogs/mock-api-based-on-openapi-asyncapi.md",
"Automation Testing in Agile Development": "resources/blogs/automation-testing-agile-development.md",
"Contract Testing": "resources/blogs/contract-testing.md",
Expand Down
1 change: 1 addition & 0 deletions docs/javascript-api/mokapi/eventhandler/httprequest.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ that contains request-specific data such as HTTP headers.
| header | object | Object contains header parameters specified by OpenAPI header parameters |
| cookie | object | Object contains cookie parameters specified by OpenAPI cookie parameters |
| body | any | Body contains request body specified by OpenAPI request body |
| api | string | The name of the API, as defined in the OpenAPI info.title field |

266 changes: 266 additions & 0 deletions docs/resources/blogs/ensuring-api-contract-compliance-with-mokapi.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
---
title: Ensure API Contract Compliance with Mokapi Validation
description: Validate HTTP API requests and responses with Mokapi to catch breaking changes early and keep backend implementations aligned with your OpenAPI spec.
image:
url: /mokapi-using-as-proxy.png
alt: Flow diagram illustrating how Mokapi enforces OpenAPI contracts between clients, Playwright tests, and backend APIs.
---

# Ensuring Compliance with the HTTP API Contract Using Mokapi for Request Forwarding and Validation

In modern distributed systems, APIs are everywhere — frontend-to-backend,
backend-to-backend, microservices communicating internally, mobile apps, test
automation tools, and more. Each interaction relies on a shared API contract,
often expressed through an OpenAPI specification. Even small
deviations can introduce bugs, break integrations, or slow down development.

By placing Mokapi between a client and a backend, you can ensure that every
**request and response adheres to your OpenAPI specification**. With a few lines
of JavaScript, Mokapi can forward requests to your backend while validating both
sides of the interaction. This provides a powerful way to enforce API correctness —
whether the client is a browser, Playwright tests, your mobile app, or even
another backend service.

In this article, I explore how Mokapi can act as **a contract-enforcing validation layer**
and why this approach benefits frontend developers, backend teams, QA engineers,
and platform engineers alike.

<img src="/mokapi-using-as-proxy.png" alt="Flow diagram illustrating how Mokapi enforces OpenAPI contracts between clients, Playwright tests, and backend APIs.">

## How to Use Mokapi for API Validation with Request Forwarding?

Mokapi cannot only be used for mocking APIs, but it can also sit between any
consumer and a backend service to validate real traffic. Using a small
JavaScript script, Mokapi can forward requests to your backend and
validates both requests and responses.


Consumer (Frontend, Playwright, Microservice) → Mokapi → Backend API

```typescript
import { on } from 'mokapi';
import { fetch } from 'mokapi/http';

/**
* This script demonstrates how to forward incoming HTTP requests
* to a real backend while letting Mokapi validate responses according
* to your OpenAPI spec.
*
* The script listens to all HTTP requests and forwards them based
* on the `request.api` field. Responses from the backend are
* validated when possible, and any errors are reported back to
* the client.
*/
export default async function () {

/**
* Register a global HTTP event handler.
* This function is called for every incoming request.
*/
on('http', async (request, response) => {

// Determine the backend URL to forward this request to
const url = getForwardUrl(request)

// If no URL could be determined, return an error immediately
if (!url) {
response.statusCode = 500;
response.body = 'Failed to forward request: unknown backend';
return;
}

try {
// Forward the request to the backend
const res = await fetch(url, {
method: request.method,
body: request.body,
headers: request.header,
timeout: '30s'
});

// Copy status code and headers from the backend response
response.statusCode = res.statusCode;
response.headers = res.headers

// Check the content type to decide whether to validate the response
const contentType = res.headers['Content-Type']?.[0] || '';

if (contentType.includes('application/json')) {
// Mokapi can validate JSON responses automatically
response.data = res.json();
} else {
// For other content types, skip validation
response.body = res.body;
}

} catch (e) {
// Handle any errors that occur while forwarding
response.statusCode = 500;
response.body = e.toString();
}
});

/**
* Maps the incoming request to a backend URL based on the API name
* defined in the OpenAPI specification (`info.title`).
* @see https://mokapi.io/docs/javascript-api/mokapi/eventhandler/httprequest
*
* @param request - the incoming Mokapi HTTP request
* @returns the full URL to forward the request to, or undefined
*/
function getForwardUrl(request: HttpRequest): string | undefined {
switch (request.api) {
case 'backend-1': {
return `https://backend1.example.com${request.url.path}?${request.url.query}`;
}
case 'backend-2': {
return `https://backend1.example.com${request.url.path}?${request.url.query}`;
}
default:
return undefined;
}
}
}
```

For each interaction, Mokapi performs four important steps:

### 1. Validates incoming requests

Mokapi checks every incoming request against your OpenAPI specification:

- HTTP method
- URL & parameters
- headers
- request body

If the client sends anything invalid, Mokapi blocks it and returns a clear
validation error.

### 2. Forwards valid requests to your backend

If the request is valid, Mokapi forwards it unchanged to the backend using JavaScript.

- No changes are required in your backend.
- No additional infrastructure is necessary.

### 3. Validates backend responses

Once the backend responds, Mokapi validates the response against the OpenAPI specification:

- status codes
- headers
- response body

If something doesn't match the contract, Mokapi blocks it and sends a validation error back to the client.

### 4. Return the validated response to the client

Only responses that pass validation reach the client, guaranteeing contract fidelity end-to-end.

## Where You Can Use Mokapi for Request Forwarding and Validation

Mokapi’s forwarding and validation capabilities make it useful far beyond local development or Playwright scripting.

### Between Frontend and Backend

Placing Mokapi between your frontend and backend ensures:
- automatic request and response validation
- immediate detection of breaking changes
- backend and API specification evolve together
- fewer “why is the frontend broken?” debugging loops

Frontend developers can experiment with confidence, knowing the backend
cannot silently diverge from the published contract.

### Between Backend Services (Service-to-Service)

In microservice architectures, API drift between services is a frequent cause of instability.
Routing service-to-service traffic through Mokapi gives you:
- strict contract enforcement between services
- early detection of incompatible changes
- stable integrations even as teams evolve independently
- clear validation errors during development and CI

Mokapi becomes a lightweight, spec-driven contract guardian across your backend ecosystem.

### In Automated Testing (e.g., Playwright)

This is one of the most powerful setups.

Playwright → Mokapi → Backend

Benefits:
- CI fails immediately when the backend breaks the API contract
- tests interact with the real backend, not mocks
- validation errors are clear and actionable
- tests remain simpler — no need to validate everything in Playwright

Your tests are guaranteed to hit a backend that actually matches the API contract.

### In Kubernetes Test Environments

Mokapi can also be used in temporary or preview environments to ensure contract validation across the entire cluster.

In Kubernetes, Mokapi can be deployed as:
- a sidecar container
- a standalone validation layer in front of backend services
- a temporary component inside preview environments

This brings:
- consistent contract validation for all cluster traffic
- early detection of breaking API changes before staging
- contract enforcement without modifying backend services
- transparent operation — apps talk to Mokapi, Mokapi talks to the backend

You can integrate Mokapi into Helm charts, GitOps workflows, or test namespaces.

## Why Teams Benefit from Using Mokapi Between Client and Backend

### Automatic Contract Enforcement

Every interaction is validated against your OpenAPI specification. Your backend can no longer quietly drift from the contract.

### Immediate Detection of Breaking Changes

Issues are caught early, not just in staging or production, such as:
- renamed or missing fields
- wrong or inconsistent formats
- unexpected status codes
- mismatched data types

### More Reliable Frontend Development

Frontend teams get:
- consistent, validated API responses
- fewer sudden breaking changes
- a smoother development workflow

This reduces context-switching and debugging time.

### Better Collaboration Between Teams

With Mokapi validating both sides:
- backend developers instantly see when they violate the contract
- frontend engineers get stable, predictable APIs
- QA gets reliable test environments
- platform engineers reduce risk during deployments

Mokapi becomes a shared API contract watchdog across the organization.

### Smooth Transition from Mocks to Real Systems

Teams often start with mocked endpoints in early development. Later, they can simply begin forwarding requests to the
real backend—while keeping validation in place.

## Conclusion

Using Mokapi between frontend and backend, between backend services, or inside Kubernetes environments provides:
- strong contract enforcement
- automatic validation for every interaction
- early detection of breaking changes
- stable multi-team integration
- more reliable CI pipelines
- a smooth path from mocking to real backend validation

Mokapi ensures your API stays aligned with its specification, no matter how quickly your system evolves.
69 changes: 62 additions & 7 deletions engine/js_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"net/url"
"strings"
"testing"
"time"

r "github.com/stretchr/testify/require"
)
Expand All @@ -23,31 +22,30 @@ func TestJsScriptEngine(t *testing.T) {
t.Run("valid", func(t *testing.T) {
t.Parallel()
e := enginetest.NewEngine()
err := e.AddScript(newScript("test.js", "export default function(){}"))
err := e.AddScript(newScript("valid.js", "export default function(){}"))
r.NoError(t, err)
r.Equal(t, 0, e.Scripts(), "no events and jobs, script should be closed")
})
t.Run("blank", func(t *testing.T) {
t.Parallel()
e := enginetest.NewEngine()
err := e.AddScript(newScript("test.js", ""))
err := e.AddScript(newScript("blank.js", ""))
r.NoError(t, err)
r.Equal(t, 0, e.Scripts(), "no events and jobs, script should be closed")
})
t.Run("typescript", func(t *testing.T) {
t.Parallel()
e := enginetest.NewEngine()
err := e.AddScript(newScript("test.ts", "const msg: string = 'Hello World';"))
err := e.AddScript(newScript("typescript.ts", "const msg: string = 'Hello World';"))
r.NoError(t, err)
r.Equal(t, 0, e.Scripts(), "no events and jobs, script should be closed")
})
t.Run("typescript async default function", func(t *testing.T) {
t.Run("async default function", func(t *testing.T) {
t.Parallel()
e := enginetest.NewEngine()
err := e.AddScript(newScript("test.js", "import { every } from 'mokapi'; export default async function(){ setTimeout(() => { every('1m', function() {}) }, 500)}"))
err := e.AddScript(newScript("async.js", "import { every } from 'mokapi'; export default async function(){ setTimeout(() => { every('1m', function() {}) }, 500)}"))
r.NoError(t, err)
r.Equal(t, 1, e.Scripts())
time.Sleep(2 * time.Second)
e.Close()
})
t.Run("script from GIT provider", func(t *testing.T) {
Expand All @@ -60,6 +58,63 @@ func TestJsScriptEngine(t *testing.T) {
r.Equal(t, 1, e.Scripts())
e.Close()
})
t.Run("example from Mokapi as proxy article", func(t *testing.T) {
t.Parallel()
e := enginetest.NewEngine()
err := e.AddScript(newScript("proxy.ts", `
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';
} else {
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
switch (res.headers['Content-Type'][0]) {
case 'application/json':
// mokapi validates the data
response.data = res.json();
default:
// mokapi skips validation
response.body = res.body;
}
} catch (e) {
response.statusCode = 500;
response.body = e.toString();
}
}
});

function getForwardUrl(request: HttpRequest): string | undefined {
switch (request.api) {
case 'backend-1': {
const url = {host: 'https://backend1.example.com', ...request.url}
return url.toString();
}
case 'backend-2': {
const url = {host: 'https://backend1.example.com', ...request.url}
return url.toString();
}
default:
return undefined;
}
}
}
`))
r.NoError(t, err)
e.Close()
})
}

func TestJsEvery(t *testing.T) {
Expand Down
Loading
Loading