Skip to content

Commit 493153b

Browse files
Enhance health check endpoint to include database status and detailed responses (#325)
1 parent de6b94a commit 493153b

2 files changed

Lines changed: 86 additions & 7 deletions

File tree

app/routers/health.py

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import logging
2-
from typing import Any
2+
from typing import Annotated
33

4-
from fastapi import APIRouter
4+
from fastapi import APIRouter, Depends
5+
from fastapi.responses import JSONResponse
56

7+
from app.container import get_database
8+
from app.db.db import Database
69

710
logger = logging.getLogger(__name__)
811
router = APIRouter()
@@ -14,10 +17,58 @@ def ok_or_error(value: bool) -> str:
1417

1518
@router.get(
1619
"/health",
17-
summary="Health check",
18-
tags=["Service Information"],
20+
summary="Health Check",
21+
description="health check for all dependent API services and components.",
22+
status_code=200,
23+
responses={
24+
200: {
25+
"description": "Health check completed (may contain unhealthy components)",
26+
"content": {
27+
"application/json": {
28+
"examples": {
29+
"all_healthy": {
30+
"summary": "All services healthy",
31+
"value": {
32+
"status": "ok",
33+
"components": {"database": "ok"},
34+
},
35+
},
36+
}
37+
}
38+
},
39+
},
40+
500: {"description": "Unexpected error during health check execution"},
41+
503: {
42+
"description": "One or more components are unhealthy",
43+
"content": {
44+
"application/json": {
45+
"examples": {
46+
"some_unhealthy": {
47+
"summary": "Some services unhealthy",
48+
"value": {
49+
"status": "error",
50+
"components": {"database": "error"},
51+
},
52+
},
53+
}
54+
}
55+
},
56+
},
57+
},
58+
tags=["Health"],
1959
)
20-
def health() -> dict[str, Any]:
21-
return {
22-
"status": "ok",
60+
def health(
61+
db: Annotated[Database, Depends(get_database)],
62+
) -> JSONResponse:
63+
logger.info("Checking application health")
64+
65+
components = {
66+
"database": ok_or_error(db.is_healthy()),
2367
}
68+
healthy = ok_or_error(all(value == "ok" for value in components.values()))
69+
content = {"status": healthy, "components": components}
70+
if healthy == "ok":
71+
return JSONResponse(content=content)
72+
unhealthy = [name for name, status in components.items() if status != "ok"]
73+
logger.warning("Health check failed for components: %s", ", ".join(unhealthy))
74+
return JSONResponse(status_code=503, content=content)

tests/test_health_router.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from pytest import MonkeyPatch
2+
from fastapi.testclient import TestClient
3+
4+
5+
def test_health_check_all_components_healthy(
6+
client: TestClient, monkeypatch: MonkeyPatch
7+
) -> None:
8+
monkeypatch.setattr("app.db.db.Database.is_healthy", lambda self: True)
9+
10+
response = client.get("/health")
11+
12+
assert response.status_code == 200
13+
body = response.json()
14+
assert body["status"] == "ok"
15+
assert body["components"]["database"] == "ok"
16+
17+
18+
def test_health_check_database_unhealthy(
19+
client: TestClient, monkeypatch: MonkeyPatch
20+
) -> None:
21+
monkeypatch.setattr("app.db.db.Database.is_healthy", lambda self: False)
22+
23+
response = client.get("/health")
24+
25+
assert response.status_code == 503
26+
body = response.json()
27+
assert body["status"] == "error"
28+
assert body["components"]["database"] == "error"

0 commit comments

Comments
 (0)