Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Application factory pattern is slow because of include_router, allow passing top level router FastAPI(router=router) #5343

Closed
9 tasks done
michaeloliverx opened this issue Sep 3, 2022 · 8 comments

Comments

@michaeloliverx
Copy link
Contributor

First Check

  • I added a very descriptive title to this issue.
  • I used the GitHub search to find a similar issue and didn't find it.
  • 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.
  • 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.

Commit to Help

  • I commit to help with one of those options 👆

Example Code

import time

from fastapi import APIRouter, FastAPI

router = APIRouter()


@router.get("/")
async def get_root():
    pass


@router.post("/")
async def post_route():
    pass


@router.patch("/")
async def patch_route():
    pass


@router.delete("/")
async def delete_route():
    pass


def create_app() -> FastAPI:
    app = FastAPI()
    app.include_router(router)
    return app

Description

We are using the application factory pattern with FastAPI as it allows us not to have the app get configured at import time. This works well for us and allows us to test the application easier.

During testing each test that needs the application gets a fresh instance by calling create_app in a pytest fixture. In an effort to speed up our test suite we performed some profiling to find the most expensive calls inside our create_app function. It turns out it was include_router, I guess its because it does a bunch of introspection and checks for each route.

After some experiments we were able to take our test suite from 2 minutes down to 15 seconds by doing this:

def create_app() -> FastAPI:
    app = FastAPI()
-   app.include_router(router)
+    router.dependency_overrides_provider = app
+    app.router = router
+    app.middleware_stack = app.build_middleware_stack()

    return app

This basically manually sets the router and rebuilds the middleware stack to avoid calling include_router again. This definitely seems like a hack but hey it works for us at the moment.

Perhaps it is a good idea to allow passing in a "top level" router to the FastAPI() constructor that would do this for us?

Wanted Solution

Possible solutions:

  1. Add functionality to add a top level router object to the application that assumes that all the necessary work (normally done in include_router) is completed already.
  2. Make include_router really fast
  3. Some other solution I haven't thought of

Wanted Code

import time

from fastapi import APIRouter, FastAPI

router = APIRouter()


@router.get("/")
async def get_root():
    pass


@router.post("/")
async def post_route():
    pass


@router.patch("/")
async def patch_route():
    pass


@router.delete("/")
async def delete_route():
    pass


def create_app() -> FastAPI:
    app = FastAPI(router=router)
    return app

Alternatives

No response

Operating System

Linux, Windows, macOS

Operating System Details

No response

FastAPI Version

0.81.0

Python Version

3.9.9

Additional Context

No response

@Kludex
Copy link
Member

Kludex commented Sep 26, 2022

The motivation here seems the test suite. Why not using a session fixture instead?

@michaeloliverx
Copy link
Contributor Author

I would like a clean application for every test, a session fixture would mean the same app instance every time.

@adarshpunj
Copy link

How do we add multiple routers in an app?

@JarroVGIT
Copy link
Contributor

@adarshpunj
Copy link

How do we add multiple routers in an app?

https://fastapi.tiangolo.com/tutorial/bigger-applications/?h=router#include-the-same-router-multiple-times-with-different-prefix

I'm asking this in context of this issue. They are suggesting to pass the router in the FastAPI app object.

@JarroVGIT
Copy link
Contributor

@adarshpunj this is an enhancement request, it is not yet possible to pass a router object to the application object.

@tiangolo
Copy link
Member

Thanks for the suggestion, but as that would mean another parameter, more maintenance, etc. And it's not something that I've seen need by anyone else, and you already solved your use case, I'll pass on this for now.

I also plan on refactoring how including routers work, that will probably also change all this.

Thanks everyone for the discussion here!

@tiangolo tiangolo added question Question or problem answered reviewed and removed feature New feature or request labels Feb 22, 2023
@tiangolo tiangolo reopened this Feb 27, 2023
@github-actions
Copy link
Contributor

Assuming the original need was handled, this will be automatically closed now. But feel free to add more comments or create new issues or PRs.

@tiangolo tiangolo reopened this Feb 27, 2023
@fastapi fastapi locked and limited conversation to collaborators Feb 27, 2023
@tiangolo tiangolo converted this issue into discussion #6302 Feb 27, 2023

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Projects
None yet
Development

No branches or pull requests

5 participants