Automatic routing does not consider root path or mount location
Expected Behavior
When embedding a Solara application in a pre-existing Starlette application, automatic routing should consider the root-path command line argument, and generate paths based on the mounted location.
Current Behavior
Automatically generated paths in the Solara app will lead to the root of the Starlette application, where the Solara app is not mounted.
Steps to Reproduce the Problem
- Create a Starlette application as defined in the Embedding in an Existing Starlette Application page.
- Mount an existing multi-page Solara app as described in the documentation page.
- Run the uvicorn server as described, optionally adding
SOLARA_ROOT_PATH="/solara"and/orSOLARA_BASE_URL="http://localhost:8865/solara/". - Navigate to the mounted location of the Solara app (e.g.
http://localhost:8865/solara). - Observe that generated links in the Solara app are located at
http://localhost/page1and nothttp://localhost/solara/page1.
An example application utilizing the multi-page demo is available here: https://github.com/nmearl/solara-test
Specifications
- Solara Version: 1.41.0
- Platform: macOS 15.1.1
- Affected Python Versions: 3.13.0
@maartenbreddels are there plans to address this issue? I've also encountered various inconsistencies when using root path in one way or another (mounting via FastAPI, using a reverse proxy prefix). Each scenario encounters one or more bugs. Great if we can continue the discussion here, and hopefully establish some resolution. This is currently blocking Solara adoption for us. See #1072 for our use case.
Scenarios
Deploying with existing FastAPI app
- I mount my Solara app on my existing FastAPI app using
app.mount("/", app=solara.server.fastapi.app)
and run it with
SOLARA_APP=main uvicorn api.main:app --port 5000
All routes work as expected.
- I mount it with
app.mount("/app", app=solara.server.fastapi.app)
and run it again. localhost:5000/app renders my home page just fine. However, my Link routes
for page, page_data in pages.items():
with solara.Link(page_data["link"]):
solara.v.Btn(class_="px-2", text=True, children=[page])
do not respect the "/app" prefix. Clicking on the "history" link, for example, takes me to 5000/history, not 5000/app/history. This is the OP's issue. However, the proposed solution does not entirely work.
I'll describe what happens with each of my routes when I apply the proposed solution:
- home - the route appears to be correct "/app/", but I get "Page not found: /app/"
- workbench - correctly goes to "/app/workbench", but Solara raises an error
ValueError: Cannot cast argument store of <function Workbench at 0x72b3c852bf40> to type WizardStore with value workbench
This is coming from the autorouting module. @maartenbreddels what was the intention of the following from this module? Why pass the route end fragment as an argument to the component?
def get_args(f):
if len(router.path_routes) < len(router.parts):
arg_strings = router.parts[len(router.path_routes) :]
args = arg_cast(arg_strings, f)
return args
else:
return []
...
if isinstance(Page, reacton.core.ComponentFunction):
args = get_args(Page)
page = Page(*args)
where Page is my Workbench component
wizard_store = WizardStore()
@solara.component
def Workbench(store: WizardStore = wizard_store):
...
- history and resources are placeholders and take no arguments. However, testing with a dummy argument yields the above issue of the workbench, so its a general issue. @nmearl I take it you didn't encounter such issues?
Deploying behind a reverse proxy prefix
My app is to be deployed in a JupyterLab environment proxied on the lab port by way of jupyter-server-proxy. I register an entry point for the proxy pointing at a function that launches the above setup (Solara app mounted on FastAPI app), here mounting the app at "/" but launching the FastAPI app with --root-path="/app", where app is my registered endpoint ("app" = "my_package.config.proxy:setup_app").
I run into the same issue as above when running jupyter-lab and clicking on my app.
It is unclear if I'm missing something, if I implemented something incorrectly, if Solara is bugged, or some combination. Great to get some feedback on this 🙏🙏🙏