Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/en/tutorial/routers.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ from fasthttp import FastHTTP

from src.clients.integrations import setup_integrations_router

app = FastHTTP()
app = FastHTTP(base_url="https://api.example.com")
app.include_router(setup_integrations_router())
```

Expand Down
2 changes: 1 addition & 1 deletion docs/ru/tutorial/routers.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ from fasthttp import FastHTTP

from src.clients.integrations import setup_integrations_router

app = FastHTTP()
app = FastHTTP(base_url="https://api.example.com")
app.include_router(setup_integrations_router())
```

Expand Down
34 changes: 27 additions & 7 deletions fasthttp/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,23 @@ async def lifespan(app: FastHTTP):
),
] = "v4",
base_url: Annotated[
str | None,
Doc(
"""
Default base URL for included routers with relative paths.

This value is used by `include_router()` when the router tree
contains relative URLs like `/users` and no explicit
`base_url` override is provided.

Example:
```python
app = FastHTTP(base_url="https://api.example.com")
```
"""
),
] = None,
docs_base_url: Annotated[
str,
Doc(
"""
Expand All @@ -288,7 +305,7 @@ async def lifespan(app: FastHTTP):

Example:
```python
app = FastHTTP(base_url="/api")
app = FastHTTP(docs_base_url="/api")
```
"""
),
Expand All @@ -300,6 +317,7 @@ async def lifespan(app: FastHTTP):
self.lifespan = lifespan
self.proxy = proxy
self.base_url = base_url
self.docs_base_url = docs_base_url
self.generate_startup_uuid = generate_startup_uuid
self.startup_uuid_version = startup_uuid_version

Expand Down Expand Up @@ -550,9 +568,11 @@ def include_router(
tags: Tags prepended before router tags.
dependencies: Dependencies prepended before router dependencies.
base_url: Base URL override for the included router tree.
If not provided, `FastHTTP.base_url` will be used.
"""
resolved_base_url = base_url if base_url is not None else self.base_url
routes = router.build_routes(
base_url=base_url,
base_url=resolved_base_url,
prefix=prefix,
tags=tags,
dependencies=dependencies,
Expand Down Expand Up @@ -1028,17 +1048,17 @@ async def index(resp):
host: Host to bind to. Default is "127.0.0.1".
port: Port to bind to. Default is 8000.
base_url: Optional prefix for documentation endpoints.
If not provided, `FastHTTP.base_url` will be used.
If not provided, `FastHTTP.docs_base_url` will be used.
"""
self.logger.info("FastHTTP started")

base_url = (
base_url if base_url is not None else self.base_url
docs_base_url = (
base_url if base_url is not None else self.docs_base_url
)
app = ASGIApp(self, base_url=base_url)
app = ASGIApp(self, base_url=docs_base_url)

server_base_url = f"http://{host}:{port}"
docs_urls = build_docs_urls(base_url)
docs_urls = build_docs_urls(docs_base_url)

print(f"\n\033[92mfasthttp\033[0m running on \033[94m{server_base_url}\033[0m")
print(
Expand Down
31 changes: 25 additions & 6 deletions tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,17 @@ def test_app_creation_with_all_parameters(self) -> None:
assert app.request_configs["GET"]["timeout"] == 60.0
assert app.request_configs["POST"]["timeout"] == 120.0

def test_app_creation_with_docs_base_url(self) -> None:
def test_app_creation_with_base_url(self) -> None:
"""Test FastHTTP creation with base_url."""
app = FastHTTP(base_url="/api")
app = FastHTTP(base_url="https://example.com")

assert app.base_url == "https://example.com"

def test_app_creation_with_docs_base_url(self) -> None:
"""Test FastHTTP creation with docs_base_url."""
app = FastHTTP(docs_base_url="/api")

assert app.base_url == "/api"
assert app.docs_base_url == "/api"

def test_app_get_decorator(self) -> None:
"""Test GET decorator registers route."""
Expand Down Expand Up @@ -170,13 +176,26 @@ async def handler(resp: Response) -> dict:
with pytest.raises(ValueError, match="Relative URL requires base_url"):
app.include_router(router)

def test_app_include_router_uses_app_base_url_by_default(self) -> None:
app = FastHTTP(base_url="https://example.com")
router = Router(prefix="/v1")

@router.get("/me")
async def handler(resp: Response) -> dict:
return resp.json()

app.include_router(router)

assert len(app.routes) == 1
assert app.routes[0].url == "https://example.com/v1/me"

@pytest.mark.asyncio
async def test_asgi_handle_request_docs_uses_app_docs_base_url(self) -> None:
"""Test ASGI docs path uses base_url from FastHTTP."""
"""Test ASGI docs path uses docs_base_url from FastHTTP."""
from fasthttp.app import ASGIApp

app = FastHTTP(base_url="/api")
asgi_app = ASGIApp(app, base_url=app.base_url)
app = FastHTTP(docs_base_url="/api")
asgi_app = ASGIApp(app, base_url=app.docs_base_url)

scope = {"type": "http", "path": "/api/docs", "method": "GET"}
receive = AsyncMock()
Expand Down
Loading