Fastapi: routes appear under tag of Apirouter and parent Apirouter

Created on 8 Dec 2020  路  13Comments  路  Source: tiangolo/fastapi

First check

  • [x] I added a very descriptive title to this issue.
  • [x] I used the GitHub search to find a similar issue and didn't find it.
  • [x] I searched the FastAPI documentation, with the integrated search.
  • [ ] I already searched in Google "How to X in FastAPI" and didn't find any information.
  • [x] I already read and followed all the tutorial in the docs and didn't find an answer.
  • [ ] I already checked if it is not related to FastAPI but to Pydantic.
  • [ ] I already checked if it is not related to FastAPI but to Swagger UI.
  • [ ] I already checked if it is not related to FastAPI but to ReDoc.
  • [ ] After submitting this, I commit to one of:

    • Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.

    • I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.

    • Implement a Pull Request for a confirmed bug.

Example

Here's a self-contained, minimal, reproducible, example with my use case:

import uvicorn
from fastapi import FastAPI, APIRouter

items = {}
app = FastAPI()

router = APIRouter(prefix="/api")
items_router = APIRouter(prefix="/items", tags=["items"])

@items_router.post("/{id}")
def add_item(id: str):
    items[id] = {"attachments": []}
    return items[id]

@items_router.get("/{id}")
def get_item(id: str):
    if not (item := items.get(id)):
        return "error"
    return item

items_attachment_router = APIRouter(prefix="/{id}", tags=["attachment"])

@items_attachment_router.post("/add")
def add_attachment(id: str, attachment: str):
    if not (item := items.get(id)):
        return "error"
    item["attachments"].append(attachment)
    return item

items_attachment_router2 = APIRouter()

@items_attachment_router2.post("/{id}/add2")
def add_attachment2(id: str, attachment: str):
    if not (item := items.get(id)):
        return "error"
    item["attachments"].append(attachment)
    return item

items_router.include_router(items_attachment_router)
items_router.include_router(items_attachment_router2, tags=["attachment2"])
router.include_router(items_router)
app.include_router(router)

if __name__ == "__main__":
    uvicorn.run(app)

Description

the routes of an APIRouter appear under the tags given by the router and all parent routers.
I implemented it the old and the new way (adding tags to the router directly & adding them as a param in include_router). Both methods put the routes add_attachment and add_attachment2 under their own tag and them items tag

  • Python version: 3.8

Additional context

FastAPI version 0.62.0

question

All 13 comments

Sorry, can you explain what the issue is? It isn't clear from the information you have provided

Please describe the issue clearly or better to share a screenshot of the page you are getting issue with along with the clean description of your issue.

Hi,

sorry, the issue is that the routes appear twice (in swagger)

https://imagebin.ca/v/5kgpOV3O3InG

This is not a bug in fastapi, please see https://github.com/swagger-api/swagger-ui/issues/3711

Hi @Mause ,
thanks for your answer. In the thread you send multiple tags are added to the endpoints. In my case the motivation is to have a large API better structured.
Different modules, different ApiRouter objects, different tags. Still using hierarchies of routers for dependency purposes.
So routes in multiple locations doesnt help a lot to have the swagger page better sorted :)

So I see, after calling

I dont see exactly where fastapi puts these tags together.
in routing include_router there is

                current_tags = []
                if tags:
                    current_tags.extend(tags)
                if route.tags:
                    current_tags.extend(route.tags)

but going through it with the debugger, there is no moment when I see the tags: _items_ and _attachment_ being put together.
Only at the point I call router.include_router(items_router) route.tags has both of them

So in order to fix this (for my app), any better way how I can change the tags of the routes than this:

...
items_router.include_router(items_attachment_router)

for route in items_router.routes:
    if route.name in [r.name for r in items_attachment_router.routes]:
        for tag in items_router.tags:
            route.tags.remove(tag)

Maybe a parameter to the include_router could avoid this hack

I'm afraid I don't understand what you are trying to achieve - if you don't want a tag to be added, don't add it in the first place?

see what the hack made:

https://imagebin.ca/v/5lIfxYIs3VRX compared to the 1. one.

while still having the hierarchy of routers, which makes a bunch of things convinient

@transfluxus isn't it the expected behavior?

In the end, all of the routers extend your actual application and its OpenAPI spec.

... sure but this is about tags, and tags being passed down from one router to another...

Why does this being about tags change anything? Everything you have described so far is expected behaviour

k, what you would expect...

Its aruably reasonable to not have all routes in the api doc multiple times and only found a hack to to solve it.

So this is a feature request

If you do not wish for routes to appear multiple times in the Swagger UI, you must only have one tag per route. We do not control this behaviour, this is functionality built into Swagger UI, as I stated above

Was this page helpful?
0 / 5 - 0 ratings

Related issues

tsdmrfth picture tsdmrfth  路  3Comments

zero0nee picture zero0nee  路  3Comments

updatatoday picture updatatoday  路  3Comments

kkinder picture kkinder  路  3Comments

vnwarrior picture vnwarrior  路  3Comments