{"id":19221060,"url":"https://github.com/midnighter/fastapi-mount","last_synced_at":"2025-05-13T01:16:04.286Z","repository":{"id":138076856,"uuid":"204085886","full_name":"Midnighter/fastapi-mount","owner":"Midnighter","description":"A minimal application for prototyping mounting the main FastAPI at a sub path.","archived":false,"fork":false,"pushed_at":"2019-08-24T18:02:43.000Z","size":21,"stargazers_count":16,"open_issues_count":1,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-05-13T01:15:51.002Z","etag":null,"topics":["fastapi","gunicorn","mount","py37","python","script-name","starlette","uvicorn","uvicorn-gunicorn"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Midnighter.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-08-24T00:02:07.000Z","updated_at":"2025-03-26T06:26:00.000Z","dependencies_parsed_at":null,"dependency_job_id":"91bb7f33-f880-40b7-8aa0-d09f96720d80","html_url":"https://github.com/Midnighter/fastapi-mount","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Midnighter%2Ffastapi-mount","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Midnighter%2Ffastapi-mount/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Midnighter%2Ffastapi-mount/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Midnighter%2Ffastapi-mount/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Midnighter","download_url":"https://codeload.github.com/Midnighter/fastapi-mount/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253851075,"owners_count":21973674,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fastapi","gunicorn","mount","py37","python","script-name","starlette","uvicorn","uvicorn-gunicorn"],"created_at":"2024-11-09T14:39:49.155Z","updated_at":"2025-05-13T01:16:04.247Z","avatar_url":"https://github.com/Midnighter.png","language":"Python","readme":"# FastAPI Mount Demonstration\n\n[![License](https://img.shields.io/badge/license-Apache--2.0-blueviolet)](https://opensource.org/licenses/Apache-2.0)\n\nA minimal application for prototyping mounting the main FastAPI at a sub path.\n\n## Rationale\n\nTypically, an application expects to be seated at the root of a domain , for\nexample, this application defines two endpoints `/hello` and `/bye` which you\ncan hit locally (after a `make start` command) at `localhost:8000/hello` and\n`localhost:8000/bye`. However, sometimes we might want to 'mount' an application\nat a sub path. This may be the case in a microservice setting where many\nservices are sitting behind a reverse proxy and should be addressed at the same\ndomain (see the figure below from https://traefik.io). The FastAPI documentation\n[explains]( https://fastapi.tiangolo.com/tutorial/sub-applications-proxy/) some\nof the problems that occur in such a situation.\n\n![traefik proxy schema](https://docs.traefik.io/img/internal.png)\n\nAs an example, let's assume you have two services `user-service` and\n`shopping-cart`. Both applications were created in a way that they expect to be\nat the root, however, now we want to address them at different paths on the same\ndomain. This could be `api.domain.com/user-service` and\n`api.domain.com/shopping-cart`. The application itself should not really be\ninvolved in this. Where it is being deployed is outside of its scope and we\ncertainly don't want to edit all our route definitions.\n\nIt turns out that the `SCRIPT_NAME` HTTP header was created for exactly this\npurpose. Instead of letting the proxy manipulate this header with every request,\nit is fairly standard to let it be configurable by an environment variable. A\npopular option is\n[gunicorn](https://docs.gunicorn.org/en/stable/faq.html#wsgi-bits) in\ncombination with\n[Flask](https://flask.palletsprojects.com/en/1.1.x/config/?highlight=script_name#APPLICATION_ROOT).\nIn Flask this is internally handled by werkzeug [as shown\nhere](https://werkzeug.palletsprojects.com/en/0.15.x/wsgi/?highlight=script_name#werkzeug.wsgi.pop_path_info).\n\n## How do we do this with FastAPI (or Starlette)?\n\n1. If you start this service without further actions you can visit the exposed\n    routes and documentation at:\n\n    * `localhost:8000/hello`\n    * `localhost:8000/bye`\n    * `localhost:8000/docs`\n    * `localhost:8000/redoc`\n\n2. In order to switch it up, we can now use a reverse proxy. One has already\n   been defined for you in the [docker-compose\n   configuration](docker-compose.yml). When you now try to access your service\n   through the proxy at `http://localhost/demo-service/hello` you will see that\n   this fails with a 404.\n\n3. We can rescue the situation by rewriting the URL in the proxy instead. You\n   can try this by instead hitting `http://localhost/demo2-service/hello`. This\n   will give you the correct response but if you now visit\n   `http://localhost/demo2-service/docs` that fails because the app itself is\n   unaware of the rewritten path.\n\n4. To try and remedy the situation, you can create a `.env` file with the\n   content:\n\n    ```\n    SCRIPT_NAME=/demo2-service\n    ```\n\n    This will automatically be used by the docker-compose configuration so just\n    restart the service (`make clean \u0026\u0026 make start`). Setting `SCRIPT_NAME` to a\n    non-empty string will affect how the application is initialized.\n\n    ```python\n    app = FastAPI(\n        title=\"FastAPI Mount Demo\",\n        description=\"A prototype of mounting the main FastAPI app under \"\n                    \"SCRIPT_NAME.\",\n        openapi_prefix=settings.SCRIPT_NAME,\n    )\n    ```\n\n    When you now visit the docs at `http://localhost/demo2-service/docs` it\n    works as expected!\n\nThis is a working strategy but it is rather tedious. We had to rewrite the URL\nin the reverse proxy and modify our application's source code (we still made it\nconfigurable) to manually set the `openapi_prefix`. There should be a better\nway.\n\n## Solution\n\nPlease take a look at the\n[root-path](https://github.com/Midnighter/fastapi-mount/tree/root-path) branch\non GitHub and the version of the README there for a proper solution\nimplementation.\n\n## Copyright\n\n* Copyright © 2019, Moritz E. Beber. All rights reserved.\n* Free software licensed under the [Apache Software License 2.0](LICENSE).\n* This README and other documentation are licensed under a [Creative Commons\n  Attribution-ShareAlike 4.0 International\n  License](http://creativecommons.org/licenses/by-sa/4.0/).\n\n  [![Creative Commons Attribution-ShareAlike 4.0 International\n  License](https://i.creativecommons.org/l/by-sa/4.0/88x31.png)](http://creativecommons.org/licenses/by-sa/4.0/)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmidnighter%2Ffastapi-mount","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmidnighter%2Ffastapi-mount","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmidnighter%2Ffastapi-mount/lists"}