diff --git a/fastapi/routing.py b/fastapi/routing.py index 21a1385a27..5df21efdba 100644 --- a/fastapi/routing.py +++ b/fastapi/routing.py @@ -1794,6 +1794,18 @@ class APIRouter(routing.Router): self.strict_content_type, ), ) + new_route = self.routes[-1] + if ( + isinstance(new_route, APIRoute) + and route.stream_item_type is not None + ): + new_route.stream_item_type = route.stream_item_type + new_route.stream_item_field = create_model_field( + name=f"StreamItem_{new_route.unique_id}", + type_=new_route.stream_item_type, + mode="serialization", + ) + new_route.app = request_response(new_route.get_route_handler()) elif isinstance(route, routing.Route): methods = list(route.methods or []) self.add_route( diff --git a/tests/test_sse.py b/tests/test_sse.py index 86a67f8f9f..f57be38e0f 100644 --- a/tests/test_sse.py +++ b/tests/test_sse.py @@ -91,9 +91,9 @@ router = APIRouter() @router.get("/events", response_class=EventSourceResponse) -async def stream_events(): - yield {"msg": "hello"} - yield {"msg": "world"} +async def stream_events() -> AsyncIterable[Item]: + yield items[0] + yield items[1] app.include_router(router, prefix="/api") @@ -265,6 +265,17 @@ def test_data_and_raw_data_mutually_exclusive(): def test_sse_on_router_included_in_app(client: TestClient): + route = next(r for r in app.routes if getattr(r, "path", None) == "/api/events") + assert route.stream_item_type is Item + assert route.stream_item_field is not None + + openapi = app.openapi() + item_schema = openapi["paths"]["/api/events"]["get"]["responses"]["200"]["content"][ + "text/event-stream" + ]["itemSchema"] + assert "contentSchema" in item_schema["properties"]["data"] + assert "Item" in openapi["components"]["schemas"] + response = client.get("/api/events") assert response.status_code == 200 assert response.headers["content-type"] == "text/event-stream; charset=utf-8"