Running Panel apps in FastAPI#

Panel generally runs on the Bokeh server, which itself runs on Tornado. However, it is also often useful to embed a Panel app in an existing web application, such as a FastAPI web server.

Since Panel 1.5.0 it is possible to run Panel application(s) natively on a FastAPI based server. Therefore this how-to guide will explain how to add Panel application(s) directly to an existing FastAPI application. This functionality is new and experimental so we also provide the original how-to guide to embed a Tornado based Panel server application inside a FastAPI application.

By the end of this guide, youโ€™ll be able to run a FastAPI application that serves a simple interactive Panel app. The Panel app will consist of a slider widget that dynamically updates a string of stars (โญ) based on the sliderโ€™s value.

Setup#

Following FastAPIโ€™s Tutorial - User Guide make sure you first have FastAPI and bokeh-fastapi installed using:

pip install panel[fastapi] 
conda install -c conda-forge bokeh-fastapi 

Create a FastAPI application#

Start by creating a FastAPI application. In this application, we will define a root endpoint that returns a simple JSON response. Open your text editor or IDE and create a file named main.py:

from fastapi import FastAPI # Initialize FastAPI application app = FastAPI() @app.get("/") async def read_root(): return {"Hello": "World"} 

Create a Panel Application#

Next we will define a simple Panel application that allows you to control the number of displayed stars with an integer slider and decorate it with the add_panel_app decorator:

import panel as pn from panel.io.fastapi import add_application @add_application('/panel', app=app, title='My Panel App') def create_panel_app(): slider = pn.widgets.IntSlider(name='Slider', start=0, end=10, value=3) return slider.rx() * 'โญ' 

Thatโ€™s it! This decorator will map a specific URL path to the Panel app, allowing it to be served as part of the FastAPI application.

The complete file should now look something like this:

import panel as pn from fastapi import FastAPI from panel.io.fastapi import add_application app = FastAPI() @app.get("/") async def read_root(): return {"Hello": "World"} @add_application('/panel', app=app, title='My Panel App') def create_panel_app(): slider = pn.widgets.IntSlider(name='Slider', start=0, end=10, value=3) return slider.rx() * 'โญ' 

Now run it with:

fastapi dev main.py 

You should see the following output:

INFO Using path main.py INFO Resolved absolute path /home/user/code/awesomeapp/main.py INFO Searching for package file structure from directories with __init__.py files INFO Importing from /home/user/code/awesomeapp/fast_api  โ•ญโ”€ Python module file โ”€โ•ฎ  โ”‚ โ”‚  โ”‚ ๐Ÿ main.py โ”‚  โ”‚ โ”‚  โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ INFO Importing module main /panel INFO Found importable FastAPI app  โ•ญโ”€ Importable FastAPI app โ”€โ•ฎ  โ”‚ โ”‚  โ”‚ from main import app โ”‚  โ”‚ โ”‚  โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ INFO Using import string main:app  โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ FastAPI CLI - Development mode โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ  โ”‚ โ”‚  โ”‚ Serving at: http://127.0.0.1:8000 โ”‚  โ”‚ โ”‚  โ”‚ API docs: http://127.0.0.1:8000/docs โ”‚  โ”‚ โ”‚  โ”‚ Running in development mode, for production use: โ”‚  โ”‚ โ”‚  โ”‚ fastapi run โ”‚  โ”‚ โ”‚  โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp/'] INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) INFO: Started reloader process [39089] using WatchFiles INFO: Started server process [39128] INFO: Waiting for application startup. INFO: Application startup complete. 

If you visit http://127.0.0.1:8000 you will see the Panel application.

Adding multiple applications#

The add_application decorator is useful when server an application defined in a function, if you want to serve multiple applications, whether they are existing Panel objects, functions, or paths to Panel application scripts you can use the add_applications function instead, e.g.:

import panel as pn from fastapi import FastAPI from panel.io.fastapi import add_applications app = FastAPI() @app.get("/") async def read_root(): return {"Hello": "World"} def create_panel_app(): slider = pn.widgets.IntSlider(name='Slider', start=0, end=10, value=3) return slider.rx() * 'โญ' add_applications({ "/panel_app1": create_panel_app, "/panel_app2": pn.Column('I am a Panel object!'), "/panel_app3": "my_panel_app.py" }, app=app) 

Tips & Tricks#

Running Behind a Proxy#

In some cases, you might be running your FastAPI app behind a reverse proxy, which adds an extra path prefix that your application doesnโ€™t directly handle. This is common when working in environments like JupyterHub or deploying to Kubernetes.

For instance, if your FastAPI / endpoint is accessed at https://some.domain/some/path/, you will need to specify the path prefix when starting your FastAPI server. To do this, use the flag --root-path /some/path/. This ensures you can access the OpenAPI docs at https://some.domain/some/path/docs.

For more details, refer to the Behind a Proxy guide.

Conclusion#

Thatโ€™s it! You now have embedded panel in FastAPI! You can now build off of this to create your own web app tailored to your needs.