Tutorial: Full-Stack SaaS Simulation
In this tutorial, we will push Mockzilla to its limits by simulating a complex SaaS application. You will learn how to build high-fidelity workflows for Subscription Management, Idempotent Payments, and Asynchronous Job Polling.
This is essential for testing how your frontend handles usage limits, duplicate requests, and background processing states without needing a real backend.
What You'll Learn
- How to simulate Usage Limits and Trial Periods.
- How to perform Idempotent Operations (like preventing duplicate charges).
- How to simulate Async Polling (Loading -> Processing -> Success).
- How to use Headers to branch account tiers.
Scenario A: The SaaS Trial (Usage Limits)
Imagine an API that allows free users only 3 requests. Once they hit the limit, they must upgrade. We use a Mini-DB table to track usage.
1. Process Request (Free Tier Active)
Triggered when a user is under the limit. It adds to a log and processes the request.
- Path:
/process - Method:
POST - Conditions:
[{"type": "lt", "field": "db.usage_log.length", "value": 3}] - Effects:
[{"type": "db.push", "table": "usage_log", "value": {"endpoint": "/process"} }] - Response:
{ "status": "success", "data": "Processed!", "requests_remaining": "{{math 3 "-" db.usage_log.length}}" }
2. Process Request (Free Tier Expired)
Triggered when the limit is reached.
- Path:
/process - Method:
POST - Conditions:
[{"type": "gt", "field": "db.usage_log.length", "value": 2}] - Response:
{ "status": 402, "error": "Trial expired. Please upgrade to Pro." }
Scenario B: Strict Idempotency (Payments)
Ensure that a destructive action (like a payment charge) only happens once per client-side request ID, even if the user clicks "Pay" twice.
1. The Duplicate Request (Cached Response)
If the exact same x-idempotency-key is sent again, return a cached success response instead of charging again. We place this condition first so it catches repeats.
- Path:
/charge - Method:
POST - Conditions:
[{"type": "contains", "field": "db.processed_payments", "value": "{{input.headers.[x-idempotency-key]}}"}] - Response:
{ "status": 200, "body": { "transaction_id": "TXN_{{input.headers.[x-idempotency-key]}}", "status": "success", "cached": true } }
2. The First Request (Charge)
Processes the payment and records the idempotency key in the database.
- Path:
/charge - Method:
POST - Conditions:
[{"type": "exists", "field": "input.headers.[x-idempotency-key]"}] - Effects:
[{"type": "db.push", "table": "processed_payments", "value": "{{input.headers.[x-idempotency-key]}}"}] - Response:
{ "status": 201, "body": { "transaction_id": "TXN_{{input.headers.[x-idempotency-key]}}", "status": "success", "cached": false } }
Scenario C: Asynchronous Job Polling
Simulate an endpoint that returns "Processing" for the first few hits, then "Completed". This is perfect for testing frontend polling logic and loading spinners.
Method: Use a hidden job_stage counter in the state.
1. Start Job
- Path:
/jobs - Method:
POST - Effects:
[{"type": "state.set", "raw": {"job_stage": 0}}] - Response:
{ "job_id": "job-123", "status": "processing" }
2. Poll Status (Processing)
- Path:
/jobs/job-123 - Method:
GET - Conditions:
[{"type": "lt", "field": "state.job_stage", "value": 2}] - Effects:
[{"type": "state.set", "raw": {"job_stage": "{{math state.job_stage "+" 1}}"}}] - Response:
{ "id": "job-123", "status": "processing" }
3. Poll Status (Completed)
- Path:
/jobs/job-123 - Method:
GET - Conditions:
[{"type": "gt", "field": "state.job_stage", "value": 1}] - Response:
{ "id": "job-123", "status": "completed", "result": "Video successfully encoded!" }
Why simulate these patterns?
1. Hard-to-Reproduce States
It is notoriously difficult to test "Out of Limits" or "Payment Failed" states in a real staging environment. With Workflows, you have deterministic control over these edge cases.
2. Testing Asynchronicity
If your frontend polls an API, you can test the Loading -> Processing -> Success transition instantly, without waiting for a real background worker to finish a 10-minute task.
3. Idempotency Validation
Verify that your frontend correctly generates and sends idempotency keys on retry attempts, ensuring users are never double-charged.
🤖 AI-First: Agent Instructions
Simulating complex SaaS systems is where Mockzilla's AI-First architecture truly shines. Instead of manually mapping every edge case, use an AI agent to build the full architecture from your product requirements or existing backend logic.
"Use mockzilla-workflow-architect to build an advanced SaaS simulation in my saas-demo scenario. It should handle trial usage limits (3 free requests), payment idempotency using the x-idempotency-key header, and an asynchronous job polling flow that transitions from 'processing' to 'completed' after 3 GET requests."
// Tip: You can even ask the AI to "Refactor this logic into a cleaner state machine" if it gets too complex.
Summary
You have now seen how Mockzilla can simulate entire system architectures.
- State is for single values and counters (like
job_stage). - Mini-DB is for collections and tracking (like
usage_logandprocessed_payments). - Conditions act as the routing brain to choose the right path based on the current context.
Pro Tip: Use the State Inspector in the Mockzilla UI to watch your variables change in real-time as your application makes API calls!
