Search code examples
pythonjupyter-notebookfastapiuvicorn

How to run FastAPI application inside Jupyter?


I am learning FastAPI and I have this example.

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

I saved the script as main.ipynb

The tutorial says to run this line of code in the command line: uvicorn main:app --reload

I am getting this error:

(venv) PS C:\Users\xxx\xxxx> uvicorn main:app --reload
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [21304] using WatchFiles
ERROR:    Error loadinimport module "main".INFO:     Stopping reloader process [21304]

The reason is because I am using .ipynb as opposed to .py.

How can i fix this error while using .ipynb.

Thanks so much


Solution

  • If you attempted to start the server as usual inside Jupyter, for example:

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

    you would get the following error:

    RuntimeError: asyncio.run() cannot be called from a running event loop
    

    This is due to Jupyter already running an event loop, and once Uvicorn calls asyncio.run() internally, the above error is raised.

    As per asyncio.run() documentation:

    This function cannot be called when another asyncio event loop is running in the same thread (see relevant asyncio implementation, where the error is raised).

    [...]

    This function always creates a new event loop and closes it at the end. It should be used as a main entry point for asyncio programs, and should ideally only be called once.

    Solution 1

    If you wouldd like to run uvicorn from an already running async environment, use uvicorn.Server.serve() instead (you could add the below to a new code cell in your Jupyter notebook, and then run it):

    import asyncio
    import uvicorn
    
    if __name__ == "__main__":
        config = uvicorn.Config(app)
        server = uvicorn.Server(config)
        await server.serve()
    

    or, get the current (running) event loop, using asyncio.get_running_loop(), and then call loop.create_task() for creating a task to run inside the event loop for the current thread:

    import asyncio
    import uvicorn
    
    if __name__ == "__main__":
        config = uvicorn.Config(app)
        server = uvicorn.Server(config)
        loop = asyncio.get_running_loop()
        loop.create_task(server.serve())
    

    Solution 2

    Alternatively, you can use nest_asyncio, which allows nested use of asyncio.run() and loop.run_until_complete():

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