Skip to content

Commit 46aca09

Browse files
YuriiMotovCopilot
andauthored
✨ Add FASTAPI_PUBLIC_URL env var to configure app's public URL that is shown on startup (#381)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 04c12c2 commit 46aca09

2 files changed

Lines changed: 54 additions & 1 deletion

File tree

src/fastapi_cli/cli.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
import os
23
from pathlib import Path
34
from typing import Annotated, Any
45

@@ -136,6 +137,7 @@ def _run(
136137
entrypoint: str | None = None,
137138
proxy_headers: bool = False,
138139
forwarded_allow_ips: str | None = None,
140+
public_url: str | None = None,
139141
) -> None:
140142
with get_rich_toolkit() as toolkit:
141143
server_type = "development" if command == "dev" else "production"
@@ -243,7 +245,7 @@ def _run(
243245
)
244246
)
245247

246-
url = f"http://{host}:{port}"
248+
url = public_url.rstrip("/") if public_url else f"http://{host}:{port}"
247249
url_docs = f"{url}/docs"
248250

249251
toolkit.print_line()
@@ -391,6 +393,7 @@ def dev(
391393
command="dev",
392394
proxy_headers=proxy_headers,
393395
forwarded_allow_ips=forwarded_allow_ips,
396+
public_url=os.getenv("FASTAPI_PUBLIC_URL"),
394397
)
395398

396399

@@ -498,6 +501,7 @@ def run(
498501
command="run",
499502
proxy_headers=proxy_headers,
500503
forwarded_allow_ips=forwarded_allow_ips,
504+
public_url=os.getenv("FASTAPI_PUBLIC_URL"),
501505
)
502506

503507

tests/test_cli.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from pathlib import Path
44
from unittest.mock import patch
55

6+
import pytest
67
import uvicorn
78
from typer.testing import CliRunner
89

@@ -424,6 +425,54 @@ def test_run_env_vars_and_args() -> None:
424425
assert "Documentation at http://0.0.0.0:8080/docs" in result.output
425426

426427

428+
@pytest.mark.parametrize("command", ["dev", "run"])
429+
@pytest.mark.parametrize(
430+
"public_url",
431+
[
432+
"https://myapp.example.com",
433+
"https://myapp.example.com/",
434+
"https://myapp.example.com/subpath/",
435+
],
436+
)
437+
def test_public_url_env_var(command: str, public_url: str) -> None:
438+
with changing_dir(assets_path):
439+
with patch.object(uvicorn, "run") as mock_run:
440+
result = runner.invoke(
441+
app,
442+
[
443+
command,
444+
"single_file_app.py",
445+
"--host",
446+
"0.0.0.0",
447+
],
448+
env={"FASTAPI_PUBLIC_URL": public_url},
449+
)
450+
assert result.exit_code == 0, result.output
451+
assert mock_run.called
452+
assert mock_run.call_args
453+
assert mock_run.call_args.kwargs == {
454+
"app": "single_file_app:app",
455+
"host": "0.0.0.0",
456+
"port": 8000,
457+
"reload": True if command == "dev" else False,
458+
"reload_dirs": None,
459+
"workers": None,
460+
"root_path": "",
461+
"proxy_headers": True,
462+
"forwarded_allow_ips": None,
463+
"log_config": get_uvicorn_log_config(),
464+
}
465+
466+
assert "Using import string: single_file_app:app" in result.output
467+
assert (
468+
f"Starting {'development' if command == 'dev' else 'production'} server 🚀"
469+
in result.output
470+
)
471+
expected_url_base = public_url.rstrip("/")
472+
assert f"Server started at {expected_url_base}" in result.output
473+
assert f"Documentation at {expected_url_base}/docs" in result.output
474+
475+
427476
def test_run_error() -> None:
428477
with changing_dir(assets_path):
429478
result = runner.invoke(app, ["run", "non_existing_file.py"])

0 commit comments

Comments
 (0)