-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Summary
The application currently fails to serve dynamically generated mock API endpoints when running in a containerized production environment. While the /generate endpoint successfully returns a 200 OK status and a URL for the new mock API, any attempt to call that URL results in a 404 Not Found error.
This issue does not occur in a simple local development environment (e.g., uvicorn main:app --reload), but is consistently reproducible in the Docker environment, even when configured to use a single worker.
Symptoms
- A
POSTrequest to/generatesuccessfully creates a mock API configuration in memory. - The application's log confirms that the route has been "registered."
- Previously, the Swagger/OpenAPI documentation at
/docswould update to show the new mock endpoint, but requests to it would fail. - All
GET,POST, etc. requests to the newly generated mock URL (e.g.,/mocks/my_api_123abc/users) return404 Not Found.
Root Cause Analysis
The core of the problem is an architectural mismatch between the application's design and the process model of a production web server like Uvicorn.
The application was designed to modify its own global state at runtime by calling app.include_router() or app.mount() after the server has already started. This approach is fundamentally unreliable due to Process Memory Isolation.
- The running Uvicorn server creates an optimized routing stack at startup.
- Lightweight, in-memory modifications to the FastAPI
appobject (like adding a route to theapp.router.routeslist) are not automatically detected by the live server loop. - The state (the list of active routes) is stored in the memory of a disposable worker process. This state can be lost during server reloads or is not shared across different processes, leading to inconsistent behavior. The result is that the server "thinks" the route doesn't exist, even though our Python code has registered it.
Proposed Solution: The Proxy/Dispatch Pattern
To solve this, we must refactor the application to stop modifying its own routes at runtime and adopt a more stable "Proxy" or "Dispatch" pattern.
-
Immutable Server: The FastAPI
appobject will be treated as immutable after startup. No new routes will be added dynamically. -
Stateful Manager: The
MockManagerservice will be redesigned. Instead of trying to register live routers, it will act as a simple, in-memory stateful registry that stores the schemas (e.g., the OpenAPI JSON) of the active mock APIs, indexed by a uniqueapi_id. -
Static Proxy Route: We will create a single, static "catch-all" route in
router.pyat startup:@router.api_route("/mocks/{api_id}/{sub_path:path}", methods=["GET", "POST", ...]) -
Dynamic Dispatch: When a request hits this proxy route, the following will happen:
- The route will capture the
api_idand thesub_path. - It will call the
MockManager's newdispatch_mock_requestmethod. - The
dispatchmethod will look up the schema for the givenapi_idin its registry. - It will then parse the schema, find the definition for the requested
sub_path, generate a fake response on the fly, and return it.
- The route will capture the
Acceptance Criteria / Tasks
- Refactor
MockManagerto store API schemas in a dictionary instead ofAPIRouterorFastAPIobjects. - Implement the
dispatch_mock_requestmethod inMockManagerto dynamically handle incoming requests based on a stored schema. - Remove all calls to
app.include_routerorapp.mountfrom the/generatelogic. - Implement the static, catch-all proxy route in
api/router.py. - Update the
/generateendpoint andGenerationServiceto use the newmock_manager.register_rest_api_schemamethod. - Acknowledge and document that as a trade-off of this stable architecture, the Swagger/OpenAPI docs will no longer show individual mock endpoints, but will instead show the single proxy route.