fastapi: OpenAPI routes do not acknowledge root_path
Also raised on https://github.com/tiangolo/fastapi/pull/26#issuecomment-562755647. See also #544.
Describe the bug
Write here a clear and concise description of what the bug is.
To Reproduce
Replace each part with your own scenario:
- Create a file with:
from fastapi import FastAPI
app = FastAPI()
@app.get("/app")
def read_root():
return {"Hello": "World"}
- Launch it using
uvicorn --root-path="bar" test_app:app - Open the browser and go to
http://127.0.0.1:8000/docs. - From the documentation, call the
GET /approute. - The doc page calls
/appand succeeds.
Expected behavior
The above test should fail after having called /bar/app, since root_path is supposed to prefix all generated URLs in case the application is served behind a reverse-proxy, among ther things. FastAPI only acknowledges openapi_prefix for the API doc.
Environment
- OS: Windows
- FastAPI Version: 0.45.0
- Python version: 3.8.0
Additional context
A similar issue applies to sub-applications:
from fastapi import FastAPI
app = FastAPI()
@app.get("/app")
def read_main():
return {"message": "Hello World from main app"}
#subapi = FastAPI(openapi_prefix="/subapi")
subapi = FastAPI()
@subapi.get("/sub")
def read_sub(request: Request):
return {
"root_path": request.scope['root_path'],
"raw_path": request.scope['raw_path'],
"path": request.scope['path'],
"app_url_for": app.url_path_for("read_sub"),
"subapp_url_for": subapi.url_path_for("read_sub"),
}
app.mount("/subapi", subapi)
{
"root_path":"bar/subapi",
"raw_path":"/subapi/sub",
"path":"/sub",
"app_url_for":"/subapi/sub",
"subapp_url_for":"/sub"
}
(url_for not being prefixed with root_path is fixed upstream by encode/starlette#699)
Unless openapi_prefix="/subapi" is passed when creating the subapplication, both http://127.0.0.1:8000/docs and http://127.0.0.1:8000/subapi/docs will point towards http://127.0.0.1:8000/openapi.json, which goes against the point of having isolated subapplications.
openapi_prefix should probably just be deprecated and assumed to match root_path if absent.
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 16
- Comments: 18 (8 by maintainers)
I feel that this is closely related to a question I was about to write, so I will post it here instead to avoid duplication.
– Hi, I’m deploying a minimal application written with FastAPI and Mangum as an adapter using AWS SAM. It works like a charm, and I’m in the process off writing an article about it to append in the fastapi documentation. Yet one thing that boggles my mind is the path of the /openapi.json and I can’t wrap my head around how to work it in different situations.
So let’s assume I have a little application like this:
I deploy it to API Gateway/Lambda to a stage called
prodso the resulting url ishttps://xxxxxxxxxx.execute-api.eu-west-1.amazonaws.com/prod. The API Gateway is set up with{proxy+}integration. Now how is it, that the/pingendpoint “knows” that the base-url is […].amazonaws.com/prod, yetopenapi.jsonassumes to be at […].amazonaws.com/openapi.json?I know I can change the prefix with
openapi_prefix="/prod"but that makes it inconvenient if I wanted to use another stage thanprod. After all I don’t have to do it for my other endpoints either. So is there a reason it doesn’t work the same way as with my other endpoints? Is it a bug, or am I just missing something very obvious?Recently ran into this issue and the best solution I could think of was monkey patching
openapi.json:Not elegant, but this did exactly what I was looking for: serving my API at
/api/v1/with swaggerui working as expected.Edit: I had to declare separate openapi functions because I wanted to protected them using basic auth, which is not used for anything else in the app (dependencies not shown above for clarity).
FYI, I’m running behind NGINX in docker-compose which looks like this:
Edit 2: the above can be achieved in a simpler way with a downside (or upside) of the “servers” drop-down appearing and the
/api/v1prefix disappearing:@sm-Fifteen @ycd Thanks guys! That’s pretty much what I ended up doing. I just wanna say the response time is amazing! I was expecting to wait 5-10 days 😃
As raised in #1294, another possibility could be to have the
serverspart of the OpenAPI document contain a single server with theroot_pathas its url (no matter how deeply the application happens to be nested), so that the rest of the routing and OpenAPI route generation logic can remain the same, while still letting OpenAPI clients (including the doc) know that all API calls should be prefixed.openapi_prefixcould then be deprecated as an alternate way of specifying the same thing, except passed directly at the application’s creation rather than as defined by the ASGI protocol, and unaccounted for when usingurl_fororurl_path_for.Therefore, using the example app from the initial post:
We would get:
/openapi.json/subapi/openapi.jsonWhich does exactly what we need in terms of ensuring the doc acknowledges the reverse proxy, and everything else just falls into place.
you can use it like that
but yeah, it’s not ideal.
Faced it when I was using uvicorn-gunicorn-fastapi-docker which was configured as nginx sub app
Example config:
@andreixk when you mount an application it basically looks into the
route.path for route in app.routes, by default your root_path is/andapp.routeslooks like thisWhen you mount another application you are just adding that route to the
app.routes, for example, let us mount/subapito our/appNow it will look like this
You are just making
/subapiaccessible from yourroot_pathso when you go to/you can access/subapisince that path is accessible from theroot_pathWhen you go to
/subapiit basically looks into thesubapi.routesso imagine if you have an endpoint called/dummyand it is declared inside ofsubapi.routes, you can access it because when you go to/subapiit becomes your route path and after when you go to an endpoint from/subapi/dummyyou can access it because it is declared insidesubapi.routesSo if you are not behind the proxy you can use prefix which simply does the same thing underneath for example, when you are including a router
It just adds a prefix when adding the paths that declared inside
APIRouterinstance, is it still confusing? Let’s keep going, imagine you have an instance of APIRouter and you want to include that router to your appFrom the main application when you are including it
This only adds a prefix when adding paths to the
app.routesSo in your case adding a prefix should be enough when including your router.
If you are still getting
Not found ....just look into your app.routes like I did above. You will probably find youropenapi.jsonis not prefixed and your other paths are prefixed or the opposite not sure.@andreixk: No,
root_pathis only useful for the application to know what to prefix URL when generating URLs for its own routes, so it only makes sense to use it if you have a proxy that’s making it so what your application sees aslocalhost:8000/hellois actually exposed to the outside world as192,168,1,123:80/api/v1/helloby a proxy. Without root_path, the application would generate route URLs that look like/helloin the documentation, and clients would understand it as meaning192,168,1,123:80/hello, which doesn’t exist.If you just want a route mounted at
/api/v1/, you probably want to have all of your routes defined on anAPIRouterinstead of your app directly, and then mount that router on your app at/api/v1, like documented here.@iwpnd Hah nice response time… I just dove through the source and started seeing that but haven’t tested it yet. Thanks for verifying that