Build a BFF (Backend for Frontend) Mock Aggregator in Python
A minimal HTTP server implementing the BFF pattern that aggregates user data and orders from two mock backends into a single JSON response.
Python code
42 linesimport json
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import urlparse
class MockBackendA:
def get_user(self, user_id):
return {"id": user_id, "name": "Alice", "service": "backend-a"}
class MockBackendB:
def get_orders(self, user_id):
return [
{"id": 1, "user_id": user_id, "item": "Laptop", "service": "backend-b"},
{"id": 2, "user_id": user_id, "item": "Mouse", "service": "backend-b"},
]
class BFFHandler(BaseHTTPRequestHandler):
def do_GET(self):
path = urlparse(self.path).path
if path.startswith("/api/user/"):
user_id = int(path.split("/")[-1])
backend_a = MockBackendA()
backend_b = MockBackendB()
response = {
"user": backend_a.get_user(user_id),
"orders": backend_b.get_orders(user_id),
}
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(json.dumps(response).encode("utf-8"))
else:
self.send_response(404)
self.end_headers()
if __name__ == "__main__":
server = HTTPServer(("localhost", 8000), BFFHandler)
print("BFF running on http://localhost:8000/api/user/1")
server.serve_forever()
Output
BFF running on http://localhost:8000/api/user/1
GET http://localhost:8000/api/user/1
{
"user": {
"id": 1,
"name": "Alice",
"service": "backend-a"
},
"orders": [
{
"id": 1,
"user_id": 1,
"item": "Laptop",
"service": "backend-b"
},
{
"id": 2,
"user_id": 1,
"item": "Mouse",
"service": "backend-b"
}
]
}
How it works
This code demonstrates the Backend for Frontend (BFF) pattern using Python's standard http.server module. The BFFHandler routes incoming GET requests and, for URLs under /api/user/, extracts the user ID and calls two mock backend classes to fetch user and order data. Both responses are then merged into a single JSON object and returned to the client. This reduces the number of round trips a frontend must make and centralizes API orchestration logic.
Common mistakes
- Forgetting to parse the URL and using the raw query string instead of the path.
- Not handling non-integer user IDs, which causes a ValueError instead of a clean 400 response.
- Assuming `self.path` doesn't contain query parameters when checking with `startswith`.
Variations
- Use `http.client` or `requests` to call real external backends instead of mock classes.
- Implement async aggregation with `asyncio` and `aiohttp` for concurrent backend calls.
Real-world use cases
- A mobile app's API gateway that combines user profile and last orders into one request to save battery and reduce latency.
- A dashboard frontend that needs aggregated stats from multiple microservices without exposing internal service URLs.
- A BFF layer that filters sensitive fields from backend responses before sending them to the browser.
Sponsored
More from System design patterns
- Builder pattern for mocking complex objects in Python easy
- Circuit Breaker Pattern in Python: Closed, Open, and Half-Open States medium
- Create a Data Helper Class in Python easy
- Domain Driven Design Aggregate Root Example in Python medium
- Facade Pattern in Python with Mock Simplification medium
- How to Aggregate Mock API Routes by Method in Python easy
Keep learning
Related tutorials and quizzes for this topic.