Schema-driven REST API simulation with FastAPI, Pydantic, and Polyfactory.
Define API behavior declaratively using schemas and dependency metadata—no endpoint logic required. Semblance is built for contract testing, prototyping, frontend development, and integration testing against realistic API simulators.
- Zero endpoint logic — Schemas and link metadata define responses
- FastAPI-native — Full OpenAPI, validation, async
- Deterministic — Seeded generation for reproducible tests
- Extensible — Custom link types via plugins
- Production-ready — Error simulation, latency, rate limiting, pagination, stateful mode, optional response validation
- Python 3.10+
- FastAPI, Pydantic, Polyfactory, Uvicorn (installed with semblance)
pip install semblanceFrom source (development):
git clone https://github.com/eddiethedean/semblance.git
cd semblance
pip install -e ".[dev]"from datetime import date, datetime
from typing import Annotated
from pydantic import BaseModel
from semblance import DateRangeFrom, FromInput, SemblanceAPI
class UserQuery(BaseModel):
name: str = "alice"
start_date: date = date(2020, 1, 1)
end_date: date = date(2025, 12, 31)
class User(BaseModel):
name: Annotated[str, FromInput("name")]
created_at: Annotated[
datetime,
DateRangeFrom("start_date", "end_date"),
]
api = SemblanceAPI(seed=42)
@api.get("/users", input=UserQuery, output=list[User], list_count=2)
def users():
pass
app = api.as_fastapi()You can register PUT, PATCH, and DELETE endpoints the same way (@api.put(...), @api.patch(...), @api.delete(..., output=None) for 204).
Run:
semblance run app:api --port 8000
# or
uvicorn app:app --reloadTry:
curl "http://127.0.0.1:8000/users?name=alice&start_date=2024-01-01&end_date=2024-12-31"Example output (with SemblanceAPI(seed=42) and list_count=2 for reproducibility):
[
{"name": "alice", "created_at": "2024-08-21T09:22:43.516168"},
{"name": "alice", "created_at": "2024-01-10T03:05:39.176702"}
]Responses are generated from your output model: name comes from the query, created_at is in the date range.
| Use Case | Description |
|---|---|
| Contract testing | Validate client behavior against a schema-accurate mock |
| Frontend development | Run a mock API for UI work without a backend |
| Prototyping | Ship realistic API shapes before implementation |
| Integration tests | Deterministic, isolated API simulators in CI |
# Scaffold a minimal app (app.py and optional semblance.yaml)
semblance init [-c] [--force]
# Validate routes and link bindings without starting a server (CI-friendly)
semblance validate module:attr
# Run a Semblance app (module:attr or just module when unambiguous)
semblance run app:api [--host HOST] [--port PORT] [--reload]
# Export OpenAPI schema (optionally with response examples)
semblance export openapi app:api [-o FILE] [--examples]
# Export OpenAPI + JSON fixtures per endpoint (GET, POST, PUT, PATCH, DELETE)
semblance export fixtures app:api [-o DIR]Runnable examples in examples/:
semblance run examples.basic.app:api --port 8000
semblance run examples.pagination.app:api --port 8000
semblance run examples.nested.app:api --port 8000
semblance run examples.stateful.app:api --port 8000
semblance run examples.advanced.app:api --port 8000
semblance run examples.error_simulation.app:api --port 8000
semblance run examples.plugins.app:api --port 8000
semblance run examples.put_patch_delete.app:api --port 8000
semblance run examples.stateful_crud.app:api --port 8000
semblance run examples.request_links.app:api --port 8000| Example | Description |
|---|---|
| basic | Minimal GET list with FromInput, DateRangeFrom |
| pagination | PageParams, PaginatedResponse |
| nested | Nested model linking |
| stateful | POST stores items, GET returns stored list |
| advanced | WhenInput, ComputedFrom, filter_by |
| error_simulation | error_rate, error_codes |
| plugins | Custom link (FromEnv) |
| put_patch_delete | PUT, PATCH, DELETE (non-stateful) |
| stateful_crud | Full stateful CRUD: GET by id, PUT, PATCH, DELETE |
| request_links | FromHeader, FromCookie |
Use the same api from Quick Start (with app = api.as_fastapi()). With the test client you get deterministic responses without starting a server:
from semblance import SemblanceAPI, test_client
# api and app from Quick Start above
client = test_client(app)
r = client.get("/users?name=testuser")
assert r.status_code == 200
data = r.json()
assert all(u["name"] == "testuser" for u in data)
# data is a list of User dicts, e.g. [{"name": "testuser", "created_at": "..."}, ...]Deterministic seeding for reproducible tests:
api = SemblanceAPI(seed=42)
# or per-endpoint: seed_from="seed" with a query paramRegister custom link types:
from semblance import register_link, SemblanceAPI
from typing import Annotated
class FromEnv:
def __init__(self, env_var: str):
self.env_var = env_var
def resolve(self, input_data, rng):
import os
return os.environ.get(self.env_var)
register_link(FromEnv)
class User(BaseModel):
name: Annotated[str, FromEnv("USER_NAME")]| Feature | Description |
|---|---|
| SemblanceAPI | GET, POST, PUT, PATCH, DELETE endpoints with input/output models |
| Links | FromInput, DateRangeFrom, WhenInput, ComputedFrom |
| Pagination | PageParams, PaginatedResponse[T] |
| Seeding | SemblanceAPI(seed=42) or seed_from="seed" |
| Error simulation | error_rate, error_codes |
| Latency | latency_ms, jitter_ms |
| Rate limiting | rate_limit=N — 429 when exceeded (per endpoint, sliding window) |
| Filtering | filter_by for list endpoints |
| Stateful mode | SemblanceAPI(stateful=True) — POST stores; GET (list + by-id), PUT/PATCH/DELETE by id use store |
| Response validation | SemblanceAPI(validate_responses=True) — verify output conforms to model |
| OpenAPI | summary, description, tags on endpoints |
| Property-based testing | semblance.property_testing: strategy_for_input_model(), test_endpoint() (Hypothesis) |
| Feature | Semblance | fastapi-mock | Prism | json-server | Schemathesis | Mockoon |
|---|---|---|---|---|---|---|
| Mock API server | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
| Python / FastAPI native | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ |
| Zero endpoint logic | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ |
| Realistic example generation | ✅ | 🟡 | ✅ | ❌ | ❌ | 🟡 |
| Input→output binding | ✅ | ❌ | ❌ | ❌ | ❌ | 🟡 |
| Deterministic seeding | ✅ | ❌ | ✅ | ❌ | ✅ | 🟡 |
| Pagination helpers | ✅ | ❌ | 🟡 | ✅ | ❌ | 🟡 |
| Error simulation | ✅ | ❌ | 🟡 | ❌ | ❌ | 🟡 |
| Stateful mode | ✅ | ❌ | ❌ | ✅ | ❌ | 🟡 |
| Extensible (plugins) | ✅ | 🟡 | ❌ | ❌ | ✅ | 🟡 |
| OpenAPI schema | ✅ | ✅ | ✅ | ❌ | ✅ | 🟡 |
| CI / pytest integration | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| Property-based testing | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ |
🟡 = partial or configurable
Full documentation: semblance.readthedocs.io
- Getting Started
- Concepts
- Input and Output Binding
- Advanced Links
- Pagination
- Simulation Options
- Stateful Mode
- CLI
- Plugins
- Request Links
- Testing
- Troubleshooting
- Cookbook
- Roadmap
- API Reference
See CONTRIBUTING.md for setup and contribution guidelines.
git clone https://github.com/eddiethedean/semblance.git
cd semblance
pip install -e ".[dev]"
pytest tests/ -vMIT License.