@@ -15,6 +15,16 @@ def fastapi_routes(self, router=None, include_default=False, expand_mounts=False
1515 router = self .app
1616 routes = []
1717 for route in router .routes :
18+ # FastAPI >= 0.137: include_router() no longer flattens child routes into
19+ # app.routes; it inserts an _IncludedRouter wrapper (a BaseRoute with no .path).
20+ # Expand it into its effective routes so introspection keeps working.
21+ if route .__class__ .__name__ == '_IncludedRouter' :
22+ routes .extend (self .fastapi_routes__from_included (route ,
23+ include_default = include_default ,
24+ expand_mounts = expand_mounts ,
25+ route_prefix = route_prefix ))
26+ continue
27+
1828 if include_default is False and route .path in FAST_API_DEFAULT_ROUTES_PATHS :
1929 continue
2030 if type (route ) is Mount :
@@ -39,4 +49,22 @@ def fastapi_routes(self, router=None, include_default=False, expand_mounts=False
3949 route_path = route_prefix + route .path
4050 route_to_add = {"http_path" : route_path , "method_name" : route .name , "http_methods" : methods }
4151 routes .append (route_to_add )
52+ return routes
53+
54+ def fastapi_routes__from_included (self , included , include_default , expand_mounts , route_prefix ):
55+ routes = []
56+ for candidate in included .effective_candidates ():
57+ if candidate .__class__ .__name__ == '_IncludedRouter' : # nested include_router
58+ routes .extend (self .fastapi_routes__from_included (candidate ,
59+ include_default = include_default ,
60+ expand_mounts = expand_mounts ,
61+ route_prefix = route_prefix ))
62+ continue
63+ path = candidate .path # effective, prefix already applied
64+ if include_default is False and path in FAST_API_DEFAULT_ROUTES_PATHS :
65+ continue
66+ methods = sorted (candidate .methods ) if getattr (candidate , 'methods' , None ) else [] # websocket → []
67+ routes .append ({"http_path" : route_prefix + path ,
68+ "method_name" : candidate .name ,
69+ "http_methods" : methods })
4270 return routes
0 commit comments