Fastapi: Mutliple OpenAPI Spec Endpoints on One App

Created on 2 Nov 2020  路  4Comments  路  Source: tiangolo/fastapi

I am working on an application that has quite a large number of endpoints. I have been using tags to group sets of API however the APIs themselves are quite different so I was wondering if there is a way of hosting multiple OpenAPI endoints.

E.g.

area1 -> /area1/swagger
area2 -> /area2/swagger

question

Most helpful comment

@ArcLightSlavik -- thanks for looking in to this 馃憤
@SebastianLuebke thanks for your solution. It worked really well 馃憤

All 4 comments

--- Not an actual solution, just some opinion / findings into how I feel it should be possible ---

I think this would work very well with APIRouter.
If you try right now you get a A response class is needed to generate OpenAPI which comes from line 161 in fastapi.openapi/utils.py:

161:    assert route.response_class, "A response class is needed to generate OpenAPI"
162:    route_response_media_type: Optional[str] = route.response_class.media_type

Removing the assert and replacing route_response_media_type with application/json actually makes it runnable, and It's actually not that far from being correct?

I've tried it with test_additional_response_extra.py test:

from fastapi import APIRouter, FastAPI
from fastapi.testclient import TestClient
from fastapi.openapi.utils import get_openapi

app = FastAPI()
router = APIRouter()
sub_router = APIRouter()


def custom_openapi():
    if app.openapi_schema:
        return app.openapi_schema
    custom_open_schema = get_openapi(
        title='FastAPI',
        version='0.1.0',
        routes=router.routes,
    )
    app.openapi_schema = custom_open_schema
    return app.openapi_schema


app.openapi = custom_openapi


@app.get("/")
def read_another_item():
    return {"lol": "yo"}


@sub_router.get("/")
def read_item():
    return {"id": "foo"}


router.include_router(sub_router, prefix="/items")

app.include_router(router)


openapi_schema = {
    'openapi': '3.0.2',
    'info': {'title': 'FastAPI', 'version': '0.1.0'},
    'paths': {
        '/items/': {
            'get': {
                'responses': {
                    '200': {
                        'description': 'Successful Response',
                        'content': {'application/json': {'schema': {'type': 'string'}}}
                    }
                },
                'summary': 'Read Item',
                'operationId': 'read_item_items__get',
            }
        }
    },
}

client = TestClient(app)


def test_openapi_schema():
    response = client.get("/openapi.json")
    assert response.status_code == 200, response.text
    assert response.json() == openapi_schema


def test_path_operation():
    response = client.get("/items/")
    assert response.status_code == 200, response.text
    assert response.json() == {"id": "foo"}

The only difference is the inclusion of {'type': 'string'} inside schema, this doesn't happen when running under app.routes.

Obviously the example isn't complex, but I don't see a reason not to have this.

thanks for this. Will look later on in the week and let you know how i go

you can create two FastAPI apps and mount them in starlette

from fastapi import FastAPI
from starlette.applications import Starlette
from starlette.routing import Mount

api1 = FastAPI(title="API1")
api2 = FastAPI(title="API2")

@api1.get("/test")
def test_api1():
    return "API1"

@api2.get("/test")
def test_api2():
    return "API2"

app = Starlette(routes=[
    Mount("/api1", api1),
    Mount("/api2", api2)
])

@ArcLightSlavik -- thanks for looking in to this 馃憤
@SebastianLuebke thanks for your solution. It worked really well 馃憤

Was this page helpful?
0 / 5 - 0 ratings