Mock API Architecture
Phony's Mock API system generates dynamic REST APIs from PGDL schemas, enabling frontend development and integration testing without backend dependencies.
Implementation status
Target design — the hosted Mock API service is not built yet. The local phony mock start server is planned as part of the CLI.
Why This Is the Hosted Piece
In Phony's local-first architecture, database sync and snapshots run in the customer's infrastructure because they touch production row data. The Mock API is the one genuinely hosted product surface — and it can be, precisely because it serves synthetic data only: responses are generated on the fly from PGDL schemas (or from anonymized snapshots a customer has explicitly exported for mocking). No customer row data is ever involved, so hosting it on Phony's edge carries none of the trust or compliance weight that made sync local-first.
The free/offline alternative is the local mock server:
phony mock start --schema api.pgdl.json --port 3000Same Rust engine, same schemas, no cloud account — hosted endpoints add a stable public URL, TLS, caching, custom domains, and team-shared configuration. Mock endpoints never serve synced production data; they serve PGDL-generated synthetic records.
Architecture Overview
┌─────────────────────────────────────────────────────────────────────────┐
│ MOCK API ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ CLIENT REQUEST │
│ ══════════════ │
│ GET https://mock.phony.cloud/ep_abc123/api/users?page=1 │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐│
│ │ EDGE (Cloudflare) ││
│ │ • TLS Termination ││
│ │ • DDoS Protection ││
│ │ • Response Caching (when enabled) ││
│ │ • Geographic routing ││
│ └───────────────────────────────┬─────────────────────────────────────┘│
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐│
│ │ MOCK API SERVER (Go) ││
│ │ ││
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ││
│ │ │ Router │ │ Schema │ │ Response │ ││
│ │ │ │─►│ Loader │─►│ Generator │ ││
│ │ │ /ep_{id}/* │ │ │ │ │ ││
│ │ └─────────────┘ └─────────────┘ └──────┬──────┘ ││
│ │ │ ││
│ │ ▼ ││
│ │ ┌─────────────┐ ││
│ │ │ Rust Core │ ││
│ │ │ (N-gram) │ ││
│ │ │ 5M rec/s │ ││
│ │ └─────────────┘ ││
│ │ ││
│ └─────────────────────────────────────────────────────────────────────┘│
│ │ │
│ ▼ │
│ RESPONSE │
│ ════════ │
│ { │
│ "data": [ │
│ { "id": "usr_abc", "name": "Mehmet", "email": "m@ex.com" }, │
│ { "id": "usr_def", "name": "Ayşe", "email": "a@ex.com" } │
│ ], │
│ "meta": { "page": 1, "total": 100 } │
│ } │
│ │
└─────────────────────────────────────────────────────────────────────────┘Endpoint Configuration
Schema-Driven Endpoints
Each endpoint is defined by a PGDL schema:
{
"endpoint": {
"path": "/api/users",
"method": "GET",
"description": "List users with pagination"
},
"schema": {
"generators": {
"id": {
"type": "logic",
"algorithm": "uuid_v7"
},
"name": {
"type": "model",
"source": "tr_TR/full_names",
"generation": { "mode": "word" }
},
"email": {
"type": "template",
"pattern": "{{lowercase(name)}}@example.com",
"unique": true
},
"created_at": {
"type": "logic",
"algorithm": "datetime_between",
"params": { "start": "-1year", "end": "now" }
}
},
"output": ["id", "name", "email", "created_at"]
},
"response": {
"wrapper": {
"data": "{{records}}",
"meta": {
"page": "{{query.page | default: 1}}",
"per_page": "{{query.per_page | default: 20}}",
"total": "{{total}}"
}
},
"count": "{{query.per_page | default: 20}}",
"seed": "{{query.seed | default: null}}"
}
}Dynamic Path Parameters
{
"endpoint": {
"path": "/api/users/:id",
"method": "GET"
},
"response": {
"count": 1,
"seed": "{{params.id}}"
}
}When accessing /api/users/123, the seed 123 ensures deterministic output - same ID always returns same user.
Request Handling
Query Parameters
| Parameter | Description | Example |
|---|---|---|
page | Page number | ?page=2 |
per_page | Items per page (max 100) | ?per_page=50 |
seed | Random seed for determinism | ?seed=12345 |
locale | Override locale | ?locale=en_US |
format | Response format | ?format=csv |
Request Body (POST/PUT)
For mutation endpoints, request body can influence response:
// POST /api/users
// Request:
{
"name": "Custom Name"
}
// Response includes submitted data + generated fields:
{
"id": "usr_generated",
"name": "Custom Name",
"email": "custom.name@example.com",
"created_at": "2026-01-31T10:00:00Z"
}Response Modes
List Mode
GET /api/users
→ Returns array of generated recordsSingle Mode
GET /api/users/:id
→ Returns single record (deterministic by ID)Streaming Mode (Large datasets)
GET /api/users?format=ndjson&count=10000
→ Returns newline-delimited JSON streamError Simulation
{
"simulation": {
"error_rate": 0.05,
"errors": [
{ "status": 500, "body": { "error": "Internal server error" }, "weight": 70 },
{ "status": 503, "body": { "error": "Service unavailable" }, "weight": 30 }
],
"latency": {
"min_ms": 50,
"max_ms": 200,
"p99_ms": 500
}
}
}Routing Architecture
URL Structure
https://mock.phony.cloud/{endpoint_id}/{path}
Examples:
https://mock.phony.cloud/ep_abc123/api/users
https://mock.phony.cloud/ep_abc123/api/users/456
https://mock.phony.cloud/ep_abc123/api/ordersCustom Domains (Business tier)
https://api-staging.yourcompany.com/users
└── CNAME → mock.phony.cloud
└── Routes to ep_abc123Multi-Endpoint Projects
A project can have multiple endpoints sharing the same base URL:
Project: "E-Commerce API"
├── GET /api/users → ep_001
├── GET /api/users/:id → ep_002
├── POST /api/users → ep_003
├── GET /api/products → ep_004
├── GET /api/orders → ep_005
└── GET /api/orders/:id/items → ep_006Caching Strategy
┌─────────────────────────────────────────────────────────────────────────┐
│ CACHING LAYERS │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ LAYER 1: Edge Cache (Cloudflare) │
│ ═══════════════════════════════ │
│ • Cache-Control: public, max-age=60 (when seed provided) │
│ • Cache key: URL + seed parameter │
│ • Bypass: No seed = no edge cache │
│ │
│ LAYER 2: Response Cache (Redis) │
│ ═══════════════════════════════ │
│ • Schema hash + seed → cached response │
│ • TTL: 5 minutes │
│ • Invalidated on schema update │
│ │
│ LAYER 3: Schema Cache (Memory) │
│ ══════════════════════════════ │
│ • Loaded PGDL schemas cached in memory │
│ • Refreshed on update webhook │
│ │
│ CACHE BEHAVIOR: │
│ ════════════════ │
│ ?seed=123 → Fully cacheable (same output every time) │
│ (no seed) → Fresh generation on each request │
│ │
└─────────────────────────────────────────────────────────────────────────┘CORS Configuration
{
"cors": {
"enabled": true,
"origins": ["*"],
"methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
"headers": ["Content-Type", "Authorization", "X-Request-ID"],
"credentials": false,
"max_age": 86400
}
}For production, restrict origins:
{
"cors": {
"origins": [
"https://app.yourcompany.com",
"http://localhost:3000"
]
}
}Authentication Simulation
API Key
{
"auth": {
"type": "api_key",
"header": "X-API-Key",
"valid_keys": ["test_key_123", "dev_key_456"],
"error_response": {
"status": 401,
"body": { "error": "Invalid API key" }
}
}
}Bearer Token
{
"auth": {
"type": "bearer",
"tokens": ["token_abc", "token_def"],
"jwt_validation": {
"enabled": true,
"issuer": "https://auth.yourcompany.com",
"audience": "api"
}
}
}Webhook Integration
On-Request Webhooks
Forward requests to your system for logging/debugging:
{
"webhooks": {
"on_request": {
"url": "https://yourapp.com/webhook/mock-request",
"include": ["headers", "query", "body"],
"async": true
}
}
}Request Logging
All requests are logged and available in dashboard:
{
"request_id": "req_abc123",
"endpoint_id": "ep_def456",
"method": "GET",
"path": "/api/users",
"query": { "page": "1" },
"response_time_ms": 45,
"status": 200,
"timestamp": "2026-01-31T10:00:00Z"
}Performance
| Metric | Value |
|---|---|
| Cold start | < 50ms |
| Warm response | < 20ms |
| Throughput | 10,000 req/sec per endpoint |
| Max response size | 10MB |
| Max records per request | 10,000 |
Rate Limits
| Tier | Requests/min | Bandwidth/day |
|---|---|---|
| Free | 100 | 100 MB |
| Starter | 1,000 | 1 GB |
| Team | 10,000 | 10 GB |
| Business | 100,000 | 100 GB |
Local Development
Run mock API locally using the CLI:
# Start local mock server
phony mock start --schema api.pgdl.json --port 3000
# Output:
# Mock API running at http://localhost:3000
# Endpoints:
# GET /api/users
# GET /api/users/:id
# POST /api/usersLocal mode uses the same Rust core as cloud, ensuring identical behavior.