Every sprint, the same story.
You need a specific test scenario — a cancelled policy, a flagged claim, an empty portfolio. You know exactly what the upstream response should look like. You've seen it in production. But you still have to raise a request with another team, wait for them to set it up, and hope it's still there when you come back.
By the time it's ready, the sprint is half over. Or the environment is down. Or someone else's test has overwritten it.
And on the bad days — the upstream itself goes down. One service, every team blocked.
The Daily Blocker
- Upstream services are shared, unstable, and often unavailable in dev environments
- One service going down blocks ALL teams depending on it
- Test data is shared — one team's test corrupts another's
- Simulating edge cases (errors, empty states, specific values) requires real environment access
- Teams are blocked waiting for other teams to finish their APIs
Result: Slow development cycles, late integration bugs, expensive UAT defects.
Introducing MockRelay
MockRelay is a transport-layer interception platform. It sits between your application and its upstream dependencies,
intercepting every outgoing HTTP call — without touching a single line of your application code.
One JAR dependency on your Spring Boot app. One npm import in your Angular app. That's the entire adoption cost.
- Intercepts outgoing HTTP calls without modifying a single line of application code
- Inject edge case scenarios — error codes, empty states, specific values — by directly editing the JSON in a dedicated UI
- Mode toggle: RECORD→ REPLAY→ PASSTHROUGH— switch to real/mock data instantly. No redeployment, no config changes, no restarts
- Teams define what their upstream responses should look like
- Framework serves controlled mock responses every time that endpoint is called
▎ "Zero config. One click. Never blocked by an upstream again."
The Kill Switch
Every service registered in MockRelay has three modes:
- RECORD — real API calls pass through to the upstream and get captured
- REPLAY — interceptor serves mock data instead of hitting the upstream
- PASSTHROUGH — interceptor steps aside completely, real calls go through unmodified
You switch between them from the UI. No redeployment. No config file changes. No restarts.
Need to test an edge case — a cancelled policy, a zero-balance account, a 500 error? Edit the mock JSON directly in the UI, reload your app, and it's there. No ticket raised, no other team involved, no environment to coordinate.
And if you ever need to verify against the real upstream — one click and you're on live data. One click back and you're on mock. It works in both directions, instantly.
Interface to manage services
MockRelay ships with a management portal where every team can see their registered APIs, inspect and edit the generated mock JSON, configure rules, and toggle modes — all in one place.
Teams don't need to touch config files or coordinate with other teams. Each team owns their mock data source completely. I will share a different post continuing on the application flow.(Also, meanwhile I will work on making it a bit visually appealing :] )
Two Ways to Deploy
This is the part most mocking tools miss.
Level 1 — UI intercepts
Angular UI → [npm interceptor] → Mock Data Service
Fast isolated UI development. The frontend team gets unblocked immediately. Trade-off: backend orchestration is not tested, only the UI layer is validated against mock data.
Level 2 — Backend intercepts
Angular UI → Real Backend → [JAR interceptor] → Mock Data Service
The backend does real orchestration against mock upstream data. The UI receives a real backend response. Only the upstream dependency is mocked — everything else runs as it would in production.
Level 2 is where MockRelay gets powerful. Your integration is fully tested. The only thing that isn't real is the upstream you don't control.
How Mock Data Is Generated
MockRelay doesn't serve static JSON files. It generates mock data through a three-pass rule engine.
Pass 1 — Auto-randomize
Every field is randomized based on type keywords and field name heuristics. A field named policyId gets a UUID. A field named creationDate gets an ISO timestamp. No configuration needed.
Pass 2 — Apply Rules
User-defined rules override specific fields. Five rule types are supported:
Only pin what matters. Everything else auto-randomizes.
Pass 3 — Business Rules
Conditional logic applied last. Written in plain English:
if status == CANCELLED then refundAmount = 0
if policyType == COMMERCIAL then premium > 10000
Rules persist permanently. Generated mock data expires in 7 days and gets regenerated on demand.
Getting Your Schema In
MockRelay supports three ways to define what your upstream response looks like:
Swagger import — point it at a Swagger URL or upload a file. MockRelay auto-detects field types and creates rules automatically, including ENUM_PICK rules for any enum properties.
Sample JSON upload(Recommended) — paste or upload a real response body. MockRelay derives the schema from it.
POJO zip upload(For non complex POJO structures) — upload a zip of your Java model classes. MockRelay parses the field declarations and builds the schema.
RECORD → TUNE → REPLAY
The rule engine is powerful but it still requires you to know what rules to define. For a large upstream with dozens of fields, that's work.
This is where RECORD mode becomes more than just a passthrough.
Switch a service to RECORD mode and real API traffic flows through and gets captured — full request and response, stored in MongoDB with a 30-day TTL. After enough real traffic is recorded (~30 exchanges is sufficient), you hit Tune.
Tune sends those real responses to an LLM (Claude via Spring AI), which analyses the actual values and suggests rules that reflect real production patterns — not random data, not manually guessed ranges, but rules derived from what your upstream actually returns.
You review the suggestions, edit if needed, and apply. Rules are saved. Recordings are deleted. Switch to REPLAY and your mock data mirrors production.
▎ "Import your schema. Record some traffic. Hit Tune. Production-accurate mock data — zero manual rule creation."
Integration in 60 Seconds
Java (Spring Boot):
mock.framework.app-id=your-app-id
mock.framework.mock-app-url=http://mockrelay:8082
One property file change. RestTemplate is automatically intercepted.
Angular:
MockRelayModule.forRoot({
mockAppUrl: 'http://mockrelay:8082',
appId: 'your-app-id',
services: [
{
urlPattern: 'https://your-upstream.com/api/**',
serviceId: 'your-upstream'
}
]
})
One module import. HttpClient is automatically intercepted.
Mode toggle — just click the toggle in the UI - no redeployment
Stack agnostic by design
MockRelay's interceptor model is transport-layer agnostic. The same central mock data service works for any client — the only thing that changes is the interceptor package.
Java teams drop in a JAR. Angular teams add an npm import. A .NET team would just need a NuGet package. Python apps a .py package. The core platform stays the same regardless of the stack.
One MockRelay instance. Every team covered — whatever they're building in.
The Before and After
The shift isn't just technical — it changes how teams work day to day.
Contract-based testing without any shared environment dependency.
Final Thoughts
I built MockRelay because the problem was real and the existing solutions required too much effort to adopt. If a tool has friction, it doesn't get used.
The goal was simple — give every team an isolated, self-service mock data source they can control from a UI, with zero changes to their application code. Real data when you need it, mock data when you don't. One click either way.
In this post I covered the architecture, the rule engine, and the RECORD → TUNE → REPLAY flow. In the next one I'll walk through the actual app — what it looks like to onboard a service, configure rules, and switch modes from the UI.
If you're on a Java/Angular stack and upstream instability is costing your team sprint velocity — follow along.
Questions, feedback, or war stories about broken upstreams — drop them in the comments.






Top comments (0)