Skip to content

Commit ee80396

Browse files
committed
add new blog article
improve readability in webui doc pages
1 parent 5e51864 commit ee80396

12 files changed

Lines changed: 456 additions & 50 deletions

File tree

docs/config.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@
201201
"description": "Explore Mokapi's resources including tutorials, examples, and blog articles. Learn to mock APIs, validate schemas, and streamline your development."
202202
},
203203
"items": {
204+
"Ensuring API Contract Compliance with Mokapi": "resources/blogs/ensuring-api-contract-compliance-with-mokapi.md",
204205
"Mock APIs based on OpenAPI and AsyncAPI": "resources/blogs/mock-api-based-on-openapi-asyncapi.md",
205206
"Automation Testing in Agile Development": "resources/blogs/automation-testing-agile-development.md",
206207
"Contract Testing": "resources/blogs/contract-testing.md",

docs/javascript-api/mokapi/eventhandler/httprequest.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ that contains request-specific data such as HTTP headers.
1818
| header | object | Object contains header parameters specified by OpenAPI header parameters |
1919
| cookie | object | Object contains cookie parameters specified by OpenAPI cookie parameters |
2020
| body | any | Body contains request body specified by OpenAPI request body |
21+
| api | string | The name of the API, as defined in the OpenAPI info.title field |
2122

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
---
2+
title: Ensure API Contract Compliance with Mokapi Validation
3+
description: Validate HTTP API requests and responses with Mokapi to catch breaking changes early and keep backend implementations aligned with your OpenAPI spec.
4+
image:
5+
url: /mokapi-using-as-proxy.png
6+
alt: Flow diagram illustrating how Mokapi enforces OpenAPI contracts between clients, Playwright tests, and backend APIs.
7+
---
8+
9+
# Ensuring Compliance with the HTTP API Contract Using Mokapi for Request Forwarding and Validation
10+
11+
In modern distributed systems, APIs are everywhere — frontend-to-backend,
12+
backend-to-backend, microservices communicating internally, mobile apps, test
13+
automation tools, and more. Each interaction relies on a shared API contract,
14+
often expressed through an OpenAPI specification. Even small
15+
deviations can introduce bugs, break integrations, or slow down development.
16+
17+
By placing Mokapi between a client and a backend, you can ensure that every
18+
**request and response adheres to your OpenAPI specification**. With a few lines
19+
of JavaScript, Mokapi can forward requests to your backend while validating both
20+
sides of the interaction. This provides a powerful way to enforce API correctness —
21+
whether the client is a browser, Playwright tests, your mobile app, or even
22+
another backend service.
23+
24+
In this article, I explore how Mokapi can act as **a contract-enforcing validation layer**
25+
and why this approach benefits frontend developers, backend teams, QA engineers,
26+
and platform engineers alike.
27+
28+
<img src="/mokapi-using-as-proxy.png" alt="Flow diagram illustrating how Mokapi enforces OpenAPI contracts between clients, Playwright tests, and backend APIs.">
29+
30+
## How to Use Mokapi for API Validation with Request Forwarding?
31+
32+
Mokapi cannot only be used for mocking APIs, but it can also sit between any
33+
consumer and a backend service to validate real traffic. Using a small
34+
JavaScript script, Mokapi can forward requests to your backend and
35+
validates both requests and responses.
36+
37+
38+
Consumer (Frontend, Playwright, Microservice) → Mokapi → Backend API
39+
40+
```typescript
41+
import { on } from 'mokapi';
42+
import { fetch } from 'mokapi/http';
43+
44+
/**
45+
* This script demonstrates how to forward incoming HTTP requests
46+
* to a real backend while letting Mokapi validate responses according
47+
* to your OpenAPI spec.
48+
*
49+
* The script listens to all HTTP requests and forwards them based
50+
* on the `request.api` field. Responses from the backend are
51+
* validated when possible, and any errors are reported back to
52+
* the client.
53+
*/
54+
export default async function () {
55+
56+
/**
57+
* Register a global HTTP event handler.
58+
* This function is called for every incoming request.
59+
*/
60+
on('http', async (request, response) => {
61+
62+
// Determine the backend URL to forward this request to
63+
const url = getForwardUrl(request)
64+
65+
// If no URL could be determined, return an error immediately
66+
if (!url) {
67+
response.statusCode = 500;
68+
response.body = 'Failed to forward request: unknown backend';
69+
return;
70+
}
71+
72+
try {
73+
// Forward the request to the backend
74+
const res = await fetch(url, {
75+
method: request.method,
76+
body: request.body,
77+
headers: request.header,
78+
timeout: '30s'
79+
});
80+
81+
// Copy status code and headers from the backend response
82+
response.statusCode = res.statusCode;
83+
response.headers = res.headers
84+
85+
// Check the content type to decide whether to validate the response
86+
const contentType = res.headers['Content-Type']?.[0] || '';
87+
88+
if (contentType.includes('application/json')) {
89+
// Mokapi can validate JSON responses automatically
90+
response.data = res.json();
91+
} else {
92+
// For other content types, skip validation
93+
response.body = res.body;
94+
}
95+
96+
} catch (e) {
97+
// Handle any errors that occur while forwarding
98+
response.statusCode = 500;
99+
response.body = e.toString();
100+
}
101+
});
102+
103+
/**
104+
* Maps the incoming request to a backend URL based on the API name
105+
* defined in the OpenAPI specification (`request.api`).
106+
*
107+
* @param request - the incoming Mokapi HTTP request
108+
* @returns the full URL to forward the request to, or undefined
109+
*/
110+
function getForwardUrl(request: HttpRequest): string | undefined {
111+
switch (request.api) {
112+
case 'backend-1': {
113+
return `https://backend1.example.com${request.url.path}?${request.url.query}`;
114+
}
115+
case 'backend-2': {
116+
return `https://backend1.example.com${request.url.path}?${request.url.query}`;
117+
}
118+
default:
119+
return undefined;
120+
}
121+
}
122+
}
123+
```
124+
125+
For each interaction, Mokapi performs four important steps:
126+
127+
### 1. Validates incoming requests
128+
129+
Mokapi checks every incoming request against your OpenAPI specification:
130+
131+
- HTTP method
132+
- URL & parameters
133+
- headers
134+
- request body
135+
136+
If the client sends anything invalid, Mokapi blocks it and returns a clear
137+
validation error.
138+
139+
### 2. Forwards valid requests to your backend
140+
141+
If the request is valid, Mokapi forwards it unchanged to the backend using JavaScript.
142+
143+
- No changes are required in your backend.
144+
- No additional infrastructure is necessary.
145+
146+
### 3. Validates backend responses
147+
148+
Once the backend responds, Mokapi validates the response against the OpenAPI specification:
149+
150+
- status codes
151+
- headers
152+
- response body
153+
154+
If something doesn't match the contract, Mokapi blocks it and sends a validation error back to the client.
155+
156+
### 4. Return the validated response to the client
157+
158+
Only responses that pass validation reach the client, guaranteeing contract fidelity end-to-end.
159+
160+
## Where You Can Use Mokapi for Request Forwarding and Validation
161+
162+
Mokapi’s forwarding and validation capabilities make it useful far beyond local development or Playwright scripting.
163+
164+
### Between Frontend and Backend
165+
166+
Placing Mokapi between your frontend and backend ensures:
167+
- automatic request and response validation
168+
- immediate detection of breaking changes
169+
- backend and API specification evolve together
170+
- fewer “why is the frontend broken?” debugging loops
171+
172+
Frontend developers can experiment with confidence, knowing the backend
173+
cannot silently diverge from the published contract.
174+
175+
### Between Backend Services (Service-to-Service)
176+
177+
In microservice architectures, API drift between services is a frequent cause of instability.
178+
Routing service-to-service traffic through Mokapi gives you:
179+
- strict contract enforcement between services
180+
- early detection of incompatible changes
181+
- stable integrations even as teams evolve independently
182+
- clear validation errors during development and CI
183+
184+
Mokapi becomes a lightweight, spec-driven contract guardian across your backend ecosystem.
185+
186+
### In Automated Testing (e.g., Playwright)
187+
188+
This is one of the most powerful setups.
189+
190+
Playwright → Mokapi → Backend
191+
192+
Benefits:
193+
- CI fails immediately when the backend breaks the API contract
194+
- tests interact with the real backend, not mocks
195+
- validation errors are clear and actionable
196+
- tests remain simpler — no need to validate everything in Playwright
197+
198+
Your tests are guaranteed to hit a backend that actually matches the API contract.
199+
200+
### In Kubernetes Test Environments
201+
202+
Mokapi can also be used in temporary or preview environments to ensure contract validation across the entire cluster.
203+
204+
In Kubernetes, Mokapi can be deployed as:
205+
- a sidecar container
206+
- a standalone validation layer in front of backend services
207+
- a temporary component inside preview environments
208+
209+
This brings:
210+
- consistent contract validation for all cluster traffic
211+
- early detection of breaking API changes before staging
212+
- contract enforcement without modifying backend services
213+
- transparent operation — apps talk to Mokapi, Mokapi talks to the backend
214+
215+
You can integrate Mokapi into Helm charts, GitOps workflows, or test namespaces.
216+
217+
## Why Teams Benefit from Using Mokapi Between Client and Backend
218+
219+
### Automatic Contract Enforcement
220+
221+
Every interaction is validated against your OpenAPI specification. Your backend can no longer quietly drift from the contract.
222+
223+
### Immediate Detection of Breaking Changes
224+
225+
Issues are caught early, not just in staging or production, such as:
226+
- renamed or missing fields
227+
- wrong or inconsistent formats
228+
- unexpected status codes
229+
- mismatched data types
230+
231+
### More Reliable Frontend Development
232+
233+
Frontend teams get:
234+
- consistent, validated API responses
235+
- fewer sudden breaking changes
236+
- a smoother development workflow
237+
238+
This reduces context-switching and debugging time.
239+
240+
### Better Collaboration Between Teams
241+
242+
With Mokapi validating both sides:
243+
- backend developers instantly see when they violate the contract
244+
- frontend engineers get stable, predictable APIs
245+
- QA gets reliable test environments
246+
- platform engineers reduce risk during deployments
247+
248+
Mokapi becomes a shared API contract watchdog across the organization.
249+
250+
### Smooth Transition from Mocks to Real Systems
251+
252+
Teams often start with mocked endpoints in early development. Later, they can simply begin forwarding requests to the
253+
real backend—while keeping validation in place.
254+
255+
## Conclusion
256+
257+
Using Mokapi between frontend and backend, between backend services, or inside Kubernetes environments provides:
258+
- strong contract enforcement
259+
- automatic validation for every interaction
260+
- early detection of breaking changes
261+
- stable multi-team integration
262+
- more reliable CI pipelines
263+
- a smooth path from mocking to real backend validation
264+
265+
Mokapi ensures your API stays aligned with its specification, no matter how quickly your system evolves.

engine/js_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,63 @@ func TestJsScriptEngine(t *testing.T) {
6060
r.Equal(t, 1, e.Scripts())
6161
e.Close()
6262
})
63+
t.Run("example from Mokapi as proxy article", func(t *testing.T) {
64+
t.Parallel()
65+
e := enginetest.NewEngine()
66+
err := e.AddScript(newScript("test.ts", `
67+
import { on } from 'mokapi';
68+
import { fetch } from 'mokapi/http';
69+
70+
export default async function () {
71+
on('http', async (request, response) => {
72+
const url = getForwardUrl(request)
73+
if (!url) {
74+
response.statusCode = 500;
75+
response.body = 'failed to forward request';
76+
} else {
77+
try {
78+
const res = await fetch(url, {
79+
method: request.method,
80+
body: request.body,
81+
headers: request.header,
82+
timeout: '30s'
83+
});
84+
response.statusCode = res.statusCode;
85+
response.headers = res.headers
86+
switch (res.headers['Content-Type'][0]) {
87+
case 'application/json':
88+
// mokapi validates the data
89+
response.data = res.json();
90+
default:
91+
// mokapi skips validation
92+
response.body = res.body;
93+
}
94+
} catch (e) {
95+
response.statusCode = 500;
96+
response.body = e.toString();
97+
}
98+
}
99+
});
100+
101+
function getForwardUrl(request: HttpRequest): string | undefined {
102+
switch (request.api) {
103+
case 'backend-1': {
104+
const url = {host: 'https://backend1.example.com', ...request.url}
105+
return url.toString();
106+
}
107+
case 'backend-2': {
108+
const url = {host: 'https://backend1.example.com', ...request.url}
109+
return url.toString();
110+
}
111+
default:
112+
return undefined;
113+
}
114+
}
115+
}
116+
`))
117+
r.NoError(t, err)
118+
e.Close()
119+
})
63120
}
64121

65122
func TestJsEvery(t *testing.T) {

js/mokapi/on.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,6 @@ func (m *Module) On(event string, do goja.Value, vArgs goja.Value) {
4242
if err != nil {
4343
return nil, err
4444
}
45-
/*for i, param := range params {
46-
e := param.Export()
47-
if p, ok := e.(*Proxy); ok {
48-
rv := reflect.ValueOf(ctx.Args[i])
49-
if rv.Kind() != reflect.Ptr {
50-
panic(m.vm.ToValue(fmt.Errorf("parameter %v is not a pointer", i)))
51-
}
52-
rv.Elem().Set(reflect.ValueOf(p.Export()))
53-
}
54-
}*/
5545
return v, nil
5646
}, &eventloop.JobContext{EventLogger: ctx.EventLogger})
5747

js/mokapi/proxy.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,10 +192,16 @@ func (p *Proxy) Keys() []string {
192192
t := target.Type()
193193
for i := 0; i < t.NumField(); i++ {
194194
f := t.Field(i)
195-
if f.PkgPath == "" {
195+
if f.PkgPath != "" {
196196
continue
197197
}
198-
result = append(result, f.Name)
198+
v := f.Tag.Get("json")
199+
if v != "" {
200+
tagValues := strings.Split(v, ",")
201+
result = append(result, tagValues[0])
202+
} else {
203+
result = append(result, f.Name)
204+
}
199205
}
200206
}
201207

0 commit comments

Comments
 (0)