From 88223ea7ca6f0fc86547ff677c0e2a6cb15c2576 Mon Sep 17 00:00:00 2001 From: giria660 Date: Thu, 26 Mar 2026 19:59:51 -0400 Subject: [PATCH] Fix mounting sub-applications under APIRouter --- fastapi/routing.py | 5 +++ tests/test_router_mount.py | 75 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 tests/test_router_mount.py diff --git a/fastapi/routing.py b/fastapi/routing.py index 36acb6b89d..a29f1880aa 100644 --- a/fastapi/routing.py +++ b/fastapi/routing.py @@ -1499,6 +1499,9 @@ class APIRouter(routing.Router): ) self.routes.append(route) + def mount(self, path: str, app: ASGIApp, name: str | None = None) -> None: + super().mount(self.prefix + path, app=app, name=name) + def websocket( self, path: Annotated[ @@ -1803,6 +1806,8 @@ class APIRouter(routing.Router): include_in_schema=route.include_in_schema, name=route.name, ) + elif isinstance(route, routing.Mount): + self.mount(prefix + route.path, route.app, route.name) elif isinstance(route, APIWebSocketRoute): current_dependencies = [] if dependencies: diff --git a/tests/test_router_mount.py b/tests/test_router_mount.py new file mode 100644 index 0000000000..2516383079 --- /dev/null +++ b/tests/test_router_mount.py @@ -0,0 +1,75 @@ +from fastapi import APIRouter, FastAPI +from fastapi.testclient import TestClient +from inline_snapshot import snapshot + + +def get_sub_app() -> FastAPI: + sub_app = FastAPI() + + @sub_app.get("/sub") + def read_sub(): + return {"message": "Hello World from sub API"} + + return sub_app + + +def test_mount_sub_application_with_router_prefix(): + router = APIRouter(prefix="/api") + router.mount("/subapi", get_sub_app()) + + app = FastAPI() + app.include_router(router) + + client = TestClient(app) + response = client.get("/api/subapi/sub") + + assert response.status_code == 200, response.text + assert response.json() == {"message": "Hello World from sub API"} + + +def test_mount_sub_application_with_include_router_prefix(): + router = APIRouter() + router.mount("/subapi", get_sub_app()) + + app = FastAPI() + app.include_router(router, prefix="/api") + + client = TestClient(app) + response = client.get("/api/subapi/sub") + + assert response.status_code == 200, response.text + assert response.json() == {"message": "Hello World from sub API"} + + +def test_mount_sub_application_openapi_with_included_router(): + router = APIRouter(prefix="/api") + router.mount("/subapi", get_sub_app()) + + app = FastAPI() + app.include_router(router) + + client = TestClient(app) + response = client.get("/api/subapi/openapi.json") + + assert response.status_code == 200, response.text + assert response.json() == snapshot( + { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/sub": { + "get": { + "summary": "Read Sub", + "operationId": "read_sub_sub_get", + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + } + }, + } + } + }, + "servers": [{"url": "/api/subapi"}], + } + )