fastapi-ht 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,30 @@
1
+ name: ci
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches:
7
+ - main
8
+
9
+ jobs:
10
+ test:
11
+ name: Run tests
12
+ runs-on: ubuntu-latest
13
+
14
+ steps:
15
+ - name: Check out repository
16
+ uses: actions/checkout@v4
17
+
18
+ - name: Set up Python
19
+ uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.12"
22
+
23
+ - name: Install uv
24
+ uses: astral-sh/setup-uv@v6
25
+
26
+ - name: Sync project dependencies
27
+ run: uv sync --dev
28
+
29
+ - name: Run test suite
30
+ run: uv run pytest
@@ -0,0 +1,33 @@
1
+ name: publish
2
+
3
+ on:
4
+ release:
5
+ types:
6
+ - published
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ publish:
11
+ name: Publish to PyPI
12
+ runs-on: ubuntu-latest
13
+ environment: pypi
14
+ permissions:
15
+ id-token: write
16
+
17
+ steps:
18
+ - name: Check out repository
19
+ uses: actions/checkout@v4
20
+
21
+ - name: Set up Python
22
+ uses: actions/setup-python@v5
23
+ with:
24
+ python-version: "3.12"
25
+
26
+ - name: Install uv
27
+ uses: astral-sh/setup-uv@v6
28
+
29
+ - name: Build distributions
30
+ run: uv build
31
+
32
+ - name: Publish distributions to PyPI
33
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,2 @@
1
+ __pycache__/
2
+ *.py[cod]
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,132 @@
1
+ Metadata-Version: 2.4
2
+ Name: fastapi-ht
3
+ Version: 0.1.0
4
+ Summary: Health check utilities for FastAPI applications.
5
+ Project-URL: Homepage, https://github.com/PinnLabs/fastapi-health-check
6
+ Project-URL: Repository, https://github.com/PinnLabs/fastapi-health-check
7
+ Project-URL: Issues, https://github.com/PinnLabs/fastapi-health-check/issues
8
+ Requires-Python: >=3.12
9
+ Requires-Dist: fastapi>=0.135.3
10
+ Requires-Dist: pydantic>=2.11.7
11
+ Description-Content-Type: text/markdown
12
+
13
+ <p align="center">
14
+ <img src="https://raw.githubusercontent.com/PinnLabs/fastapi-health-check/main/public/logo.png" alt="fastapi-health-check logo" width="520" style="display: block; margin:
15
+ 0 auto;" />
16
+ </p>
17
+
18
+ # fastapi-health-check
19
+
20
+ FastAPI health checks with a small public API, a visual status page, and JSON responses from the same endpoint.
21
+
22
+ ### Example interface
23
+
24
+ ![fastapi-health-check example interface](https://raw.githubusercontent.com/PinnLabs/fastapi-health-check/main/public/example_use.png)
25
+
26
+ ## What the library provides
27
+
28
+ - A base contract for advanced checks
29
+ - A lightweight registry for collecting and running checks
30
+ - A single `/ht` endpoint with HTML by default
31
+ - JSON responses when the client sends `Accept: application/json`
32
+ - A simple way to monitor any custom area of your system
33
+
34
+ ## Important note
35
+
36
+ The library does not ship with a database check by default.
37
+
38
+ The only built-in check today is `AppAliveCheck`, which reports that the application is up. Database, Redis, queues, external APIs, or any other monitored area are meant to be registered by the user.
39
+
40
+ ## Quick start
41
+
42
+ ```python
43
+ from fastapi import FastAPI
44
+
45
+ from fastapi_health_check import AppAliveCheck, HealthRegistry, health_check, install_health_check
46
+
47
+
48
+ app = FastAPI()
49
+ registry = HealthRegistry(
50
+ [
51
+ AppAliveCheck(),
52
+ health_check("database", lambda: "connection ok"),
53
+ health_check("redis", lambda: "cache reachable"),
54
+ ]
55
+ )
56
+
57
+ install_health_check(app, registry)
58
+ ```
59
+
60
+ This exposes `GET /ht`.
61
+
62
+ - In a browser, the route renders an HTML health page
63
+ - For automated integrations, the same route returns JSON when the client sends `Accept: application/json`
64
+
65
+ ## Monitoring custom areas
66
+
67
+ If you want to monitor anything beyond the built-in app liveness check, the easiest option is the `health_check()` factory.
68
+
69
+ You can use it for:
70
+
71
+ - databases
72
+ - Redis or cache layers
73
+ - background queues
74
+ - external APIs
75
+ - storage services
76
+ - internal domain-specific dependencies
77
+
78
+ ### Synchronous checks
79
+
80
+ ```python
81
+ from fastapi_health_check import health_check
82
+
83
+ database_check = health_check("database", lambda: "connection ok")
84
+ redis_check = health_check("redis", lambda: "cache reachable")
85
+ ```
86
+
87
+ ### Asynchronous checks
88
+
89
+ ```python
90
+ from fastapi_health_check import health_check
91
+
92
+
93
+ async def payments_api_check() -> str | None:
94
+ return "payments API available"
95
+
96
+
97
+ payments_check = health_check("payments_api", payments_api_check)
98
+ ```
99
+
100
+ ### Class-based checks for advanced cases
101
+
102
+ ```python
103
+ from fastapi_health_check import HealthCheck
104
+
105
+
106
+ class QueueCheck(HealthCheck):
107
+ default_name = "queue"
108
+
109
+ async def check(self) -> str | None:
110
+ return "queue connected"
111
+ ```
112
+
113
+ Use class-based checks when you want:
114
+
115
+ - dependency injection through `__init__`
116
+ - reusable state
117
+ - more structured custom behavior
118
+
119
+ ## Local manual testing
120
+
121
+ The repository includes a local example application at `src/examples/basic_app.py`.
122
+
123
+ Run it with:
124
+
125
+ ```bash
126
+ uv run uvicorn src.examples.basic_app:app --reload
127
+ ```
128
+
129
+ Then open:
130
+
131
+ - `http://127.0.0.1:8000/ht` for the HTML page
132
+ - `curl -H "Accept: application/json" http://127.0.0.1:8000/ht` for JSON
@@ -0,0 +1,120 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/PinnLabs/fastapi-health-check/main/public/logo.png" alt="fastapi-health-check logo" width="520" style="display: block; margin:
3
+ 0 auto;" />
4
+ </p>
5
+
6
+ # fastapi-health-check
7
+
8
+ FastAPI health checks with a small public API, a visual status page, and JSON responses from the same endpoint.
9
+
10
+ ### Example interface
11
+
12
+ ![fastapi-health-check example interface](https://raw.githubusercontent.com/PinnLabs/fastapi-health-check/main/public/example_use.png)
13
+
14
+ ## What the library provides
15
+
16
+ - A base contract for advanced checks
17
+ - A lightweight registry for collecting and running checks
18
+ - A single `/ht` endpoint with HTML by default
19
+ - JSON responses when the client sends `Accept: application/json`
20
+ - A simple way to monitor any custom area of your system
21
+
22
+ ## Important note
23
+
24
+ The library does not ship with a database check by default.
25
+
26
+ The only built-in check today is `AppAliveCheck`, which reports that the application is up. Database, Redis, queues, external APIs, or any other monitored area are meant to be registered by the user.
27
+
28
+ ## Quick start
29
+
30
+ ```python
31
+ from fastapi import FastAPI
32
+
33
+ from fastapi_health_check import AppAliveCheck, HealthRegistry, health_check, install_health_check
34
+
35
+
36
+ app = FastAPI()
37
+ registry = HealthRegistry(
38
+ [
39
+ AppAliveCheck(),
40
+ health_check("database", lambda: "connection ok"),
41
+ health_check("redis", lambda: "cache reachable"),
42
+ ]
43
+ )
44
+
45
+ install_health_check(app, registry)
46
+ ```
47
+
48
+ This exposes `GET /ht`.
49
+
50
+ - In a browser, the route renders an HTML health page
51
+ - For automated integrations, the same route returns JSON when the client sends `Accept: application/json`
52
+
53
+ ## Monitoring custom areas
54
+
55
+ If you want to monitor anything beyond the built-in app liveness check, the easiest option is the `health_check()` factory.
56
+
57
+ You can use it for:
58
+
59
+ - databases
60
+ - Redis or cache layers
61
+ - background queues
62
+ - external APIs
63
+ - storage services
64
+ - internal domain-specific dependencies
65
+
66
+ ### Synchronous checks
67
+
68
+ ```python
69
+ from fastapi_health_check import health_check
70
+
71
+ database_check = health_check("database", lambda: "connection ok")
72
+ redis_check = health_check("redis", lambda: "cache reachable")
73
+ ```
74
+
75
+ ### Asynchronous checks
76
+
77
+ ```python
78
+ from fastapi_health_check import health_check
79
+
80
+
81
+ async def payments_api_check() -> str | None:
82
+ return "payments API available"
83
+
84
+
85
+ payments_check = health_check("payments_api", payments_api_check)
86
+ ```
87
+
88
+ ### Class-based checks for advanced cases
89
+
90
+ ```python
91
+ from fastapi_health_check import HealthCheck
92
+
93
+
94
+ class QueueCheck(HealthCheck):
95
+ default_name = "queue"
96
+
97
+ async def check(self) -> str | None:
98
+ return "queue connected"
99
+ ```
100
+
101
+ Use class-based checks when you want:
102
+
103
+ - dependency injection through `__init__`
104
+ - reusable state
105
+ - more structured custom behavior
106
+
107
+ ## Local manual testing
108
+
109
+ The repository includes a local example application at `src/examples/basic_app.py`.
110
+
111
+ Run it with:
112
+
113
+ ```bash
114
+ uv run uvicorn src.examples.basic_app:app --reload
115
+ ```
116
+
117
+ Then open:
118
+
119
+ - `http://127.0.0.1:8000/ht` for the HTML page
120
+ - `curl -H "Accept: application/json" http://127.0.0.1:8000/ht` for JSON
Binary file
Binary file
@@ -0,0 +1,35 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "fastapi-ht"
7
+ version = "0.1.0"
8
+ description = "Health check utilities for FastAPI applications."
9
+ readme = "README.md"
10
+ requires-python = ">=3.12"
11
+ dependencies = [
12
+ "fastapi>=0.135.3",
13
+ "pydantic>=2.11.7",
14
+ ]
15
+
16
+ [project.urls]
17
+ Homepage = "https://github.com/PinnLabs/fastapi-health-check"
18
+ Repository = "https://github.com/PinnLabs/fastapi-health-check"
19
+ Issues = "https://github.com/PinnLabs/fastapi-health-check/issues"
20
+
21
+ [dependency-groups]
22
+ dev = [
23
+ "httpx>=0.28.1",
24
+ "mypy>=1.20.0",
25
+ "pytest>=9.0.3",
26
+ "ruff>=0.15.9",
27
+ "uvicorn>=0.44.0",
28
+ ]
29
+
30
+ [tool.pytest.ini_options]
31
+ pythonpath = ["src"]
32
+ testpaths = ["tests"]
33
+
34
+ [tool.hatch.build.targets.wheel]
35
+ packages = ["src/fastapi_health_check"]
@@ -0,0 +1,19 @@
1
+ from fastapi import FastAPI
2
+ from fastapi_health_check import (
3
+ AppAliveCheck,
4
+ HealthRegistry,
5
+ health_check,
6
+ install_health_check,
7
+ )
8
+
9
+
10
+ app = FastAPI()
11
+ registry = HealthRegistry(
12
+ [
13
+ AppAliveCheck(),
14
+ health_check("database", lambda: "connection ok"),
15
+ health_check("redis", lambda: "cache reachable"),
16
+ ]
17
+ )
18
+
19
+ install_health_check(app, registry)
@@ -0,0 +1,17 @@
1
+ from fastapi_health_check.checks import AppAliveCheck, FunctionHealthCheck, HealthCheck, health_check
2
+ from fastapi_health_check.integration.fastapi import install_health_check
3
+ from fastapi_health_check.models import HealthCheckResult, HealthReport
4
+ from fastapi_health_check.registry import HealthRegistry
5
+ from fastapi_health_check.ui import render_health_report_page
6
+
7
+ __all__ = [
8
+ "AppAliveCheck",
9
+ "FunctionHealthCheck",
10
+ "HealthCheck",
11
+ "HealthCheckResult",
12
+ "HealthRegistry",
13
+ "HealthReport",
14
+ "health_check",
15
+ "install_health_check",
16
+ "render_health_report_page",
17
+ ]
@@ -0,0 +1 @@
1
+ """Static assets for the health check UI."""
@@ -0,0 +1,178 @@
1
+ :root {
2
+ color-scheme: light;
3
+ --bg: #f4f8fb;
4
+ --panel: #ffffff;
5
+ --panel-border: #d7e4e9;
6
+ --text: #1d2733;
7
+ --muted: #5b6975;
8
+ --ok: #009688;
9
+ --ok-soft: #d8f2ee;
10
+ --fail: #e15656;
11
+ --fail-soft: #ffe2e2;
12
+ --accent: #0b7fab;
13
+ --accent-soft: rgba(11, 127, 171, 0.14);
14
+ --shadow: 0 22px 50px rgba(15, 23, 42, 0.08);
15
+ }
16
+
17
+ * {
18
+ box-sizing: border-box;
19
+ }
20
+
21
+ body {
22
+ margin: 0;
23
+ min-height: 100vh;
24
+ font-family: "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif;
25
+ color: var(--text);
26
+ background:
27
+ radial-gradient(circle at top left, var(--accent-soft), transparent 30%),
28
+ linear-gradient(180deg, #ffffff 0%, var(--bg) 100%);
29
+ }
30
+
31
+ .page {
32
+ width: min(960px, calc(100% - 32px));
33
+ margin: 0 auto;
34
+ padding: 56px 0 72px;
35
+ }
36
+
37
+ .hero {
38
+ display: grid;
39
+ gap: 18px;
40
+ margin-bottom: 28px;
41
+ }
42
+
43
+ .eyebrow {
44
+ margin: 0;
45
+ color: var(--accent);
46
+ font-size: 0.9rem;
47
+ font-weight: 700;
48
+ letter-spacing: 0.08em;
49
+ text-transform: uppercase;
50
+ }
51
+
52
+ h1 {
53
+ margin: 0;
54
+ font-size: clamp(2rem, 4vw, 3.35rem);
55
+ line-height: 1.02;
56
+ }
57
+
58
+ .subtitle {
59
+ margin: 0;
60
+ max-width: 42rem;
61
+ color: var(--muted);
62
+ font-size: 1rem;
63
+ line-height: 1.6;
64
+ }
65
+
66
+ .summary {
67
+ display: grid;
68
+ grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
69
+ gap: 16px;
70
+ margin-bottom: 24px;
71
+ }
72
+
73
+ .summary-card,
74
+ .check-card {
75
+ background: var(--panel);
76
+ border: 1px solid var(--panel-border);
77
+ border-radius: 20px;
78
+ box-shadow: var(--shadow);
79
+ }
80
+
81
+ .summary-card {
82
+ padding: 22px;
83
+ }
84
+
85
+ .summary-label {
86
+ margin: 0 0 10px;
87
+ color: var(--muted);
88
+ font-size: 0.9rem;
89
+ text-transform: uppercase;
90
+ letter-spacing: 0.06em;
91
+ }
92
+
93
+ .summary-value {
94
+ margin: 0;
95
+ font-size: 1.8rem;
96
+ font-weight: 700;
97
+ }
98
+
99
+ .badge {
100
+ display: inline-flex;
101
+ align-items: center;
102
+ gap: 8px;
103
+ width: fit-content;
104
+ padding: 8px 12px;
105
+ border-radius: 999px;
106
+ font-weight: 700;
107
+ }
108
+
109
+ .badge.ok {
110
+ color: var(--ok);
111
+ background: var(--ok-soft);
112
+ }
113
+
114
+ .badge.fail {
115
+ color: var(--fail);
116
+ background: var(--fail-soft);
117
+ }
118
+
119
+ .checks {
120
+ display: grid;
121
+ gap: 16px;
122
+ }
123
+
124
+ .check-card {
125
+ padding: 20px 22px;
126
+ }
127
+
128
+ .check-header {
129
+ display: flex;
130
+ flex-wrap: wrap;
131
+ justify-content: space-between;
132
+ gap: 12px;
133
+ margin-bottom: 12px;
134
+ }
135
+
136
+ .check-name {
137
+ margin: 0;
138
+ font-size: 1.1rem;
139
+ }
140
+
141
+ .check-meta {
142
+ display: flex;
143
+ flex-wrap: wrap;
144
+ gap: 10px;
145
+ align-items: center;
146
+ }
147
+
148
+ .duration {
149
+ color: var(--muted);
150
+ font-variant-numeric: tabular-nums;
151
+ }
152
+
153
+ .message {
154
+ margin: 0;
155
+ color: var(--muted);
156
+ line-height: 1.6;
157
+ }
158
+
159
+ .empty {
160
+ padding: 28px;
161
+ text-align: center;
162
+ color: var(--muted);
163
+ background: rgba(255, 255, 255, 0.72);
164
+ border: 1px dashed var(--panel-border);
165
+ border-radius: 20px;
166
+ }
167
+
168
+ @media (max-width: 640px) {
169
+ .page {
170
+ width: min(100% - 24px, 960px);
171
+ padding-top: 32px;
172
+ }
173
+
174
+ .summary-card,
175
+ .check-card {
176
+ border-radius: 16px;
177
+ }
178
+ }
@@ -0,0 +1,33 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>{{ title }}</title>
7
+ <style>
8
+ {{ styles }}
9
+ </style>
10
+ </head>
11
+ <body>
12
+ <main class="page">
13
+ <section class="hero">
14
+ <p class="eyebrow">FastAPI Health Check</p>
15
+ <h1>{{ title }}</h1>
16
+ <p class="subtitle">A compact operational view of backend dependencies, designed for quick status inspection.</p>
17
+ </section>
18
+ <section class="summary">
19
+ <article class="summary-card">
20
+ <p class="summary-label">Overall status</p>
21
+ <div class="badge {{ summary_class }}">{{ summary_label }}</div>
22
+ </article>
23
+ <article class="summary-card">
24
+ <p class="summary-label">Checks</p>
25
+ <p class="summary-value">{{ checks_count }}</p>
26
+ </article>
27
+ </section>
28
+ <section class="checks">
29
+ {{ checks_markup }}
30
+ </section>
31
+ </main>
32
+ </body>
33
+ </html>
@@ -0,0 +1,70 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Awaitable, Callable
4
+ from inspect import isawaitable
5
+ from abc import ABC, abstractmethod
6
+ from time import perf_counter
7
+
8
+ from fastapi_health_check.models import HealthCheckResult
9
+
10
+ CheckHandler = Callable[[], str | None | Awaitable[str | None]]
11
+
12
+
13
+ class HealthCheck(ABC):
14
+ default_name = ""
15
+
16
+ def __init__(self, name: str | None = None) -> None:
17
+ resolved_name = name or self.default_name or self.__class__.__name__.removesuffix("Check").lower()
18
+ if not resolved_name:
19
+ msg = "health checks must define a name"
20
+ raise ValueError(msg)
21
+
22
+ self.name = resolved_name
23
+
24
+ async def run(self) -> HealthCheckResult:
25
+ started_at = perf_counter()
26
+
27
+ try:
28
+ message = await self.check()
29
+ except Exception as exc:
30
+ return HealthCheckResult(
31
+ name=self.name,
32
+ status="fail",
33
+ message=str(exc),
34
+ duration_ms=round((perf_counter() - started_at) * 1000, 3),
35
+ )
36
+
37
+ return HealthCheckResult(
38
+ name=self.name,
39
+ status="ok",
40
+ message=message,
41
+ duration_ms=round((perf_counter() - started_at) * 1000, 3),
42
+ )
43
+
44
+ @abstractmethod
45
+ async def check(self) -> str | None:
46
+ """Execute the health check and return an optional success message."""
47
+
48
+
49
+ class AppAliveCheck(HealthCheck):
50
+ default_name = "app_alive"
51
+
52
+ async def check(self) -> str | None:
53
+ return None
54
+
55
+
56
+ class FunctionHealthCheck(HealthCheck):
57
+ def __init__(self, name: str, handler: CheckHandler) -> None:
58
+ super().__init__(name=name)
59
+ self._handler = handler
60
+
61
+ async def check(self) -> str | None:
62
+ result = self._handler()
63
+ if isawaitable(result):
64
+ return await result
65
+
66
+ return result
67
+
68
+
69
+ def health_check(name: str, handler: CheckHandler) -> FunctionHealthCheck:
70
+ return FunctionHealthCheck(name=name, handler=handler)
@@ -0,0 +1,3 @@
1
+ from fastapi_health_check.integration.fastapi import install_health_check
2
+
3
+ __all__ = ["install_health_check"]