Repository: nanotaboada/python-samples-fastapi-restful
Pull request #364: feat(routes): add health check endpoint
Author: login: nanotaboada
Created: 2025-05-18T00:41:45Z | Merged: 2025-05-18T00:51:53Z



<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/nanotaboada/python-samples-fastapi-restful/364)
<!-- Reviewable:end -->


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **New Features**
  - Added a `/health` endpoint to provide a simple health check for the service.
- **Chores**
  - Updated container configuration to include health check support and improved metadata.
- **Tests**
  - Introduced a test to verify the `/health` endpoint responds with status 200 and expected output.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/Dockerfile b/Dockerfile
index 4b97e93..20975d3 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -3,6 +3,7 @@
 # This stage builds the application and its dependencies.
 # ------------------------------------------------------------------------------
 FROM python:3.13.3-slim-bookworm AS builder
+
 WORKDIR /app
 
 # Install system build tools for packages with native extensions
@@ -10,7 +11,7 @@ RUN apt-get update && \
     apt-get install -y --no-install-recommends build-essential gcc libffi-dev libssl-dev && \
     rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*.deb
 
-# Pre-build all dependencies into wheels for reproducibility and speed
+# Build all dependencies into wheels for reproducibility and speed
 COPY --chown=root:root --chmod=644 requirements.txt .
 RUN pip wheel --no-cache-dir --wheel-dir=/app/wheelhouse -r requirements.txt
 
@@ -19,49 +20,59 @@ RUN pip wheel --no-cache-dir --wheel-dir=/app/wheelhouse -r requirements.txt
 # This stage creates the final, minimal image to run the application.
 # ------------------------------------------------------------------------------
 FROM python:3.13.3-slim-bookworm AS runtime
+
 WORKDIR /app
 
-# Metadata labels
+# Install curl for health check
+RUN apt-get update && apt-get install -y --no-install-recommends curl && \
+    rm -rf /var/lib/apt/lists/*
+
+# Add metadata labels
 LABEL org.opencontainers.image.title="🧪 RESTful API with Python 3 and FastAPI"
 LABEL org.opencontainers.image.description="Proof of Concept for a RESTful API made with Python 3 and FastAPI"
 LABEL org.opencontainers.image.licenses="MIT"
 LABEL org.opencontainers.image.source="https://github.com/nanotaboada/python-samples-fastapi-restful"
 
-# Copy prebuilt wheels and install dependencies
-COPY --chown=root:root --chmod=644 requirements.txt .
-COPY --from=builder --chown=root:root --chmod=755 /app/wheelhouse /app/wheelhouse
+# Copy metadata docs for container registries (e.g.: GitHub Container Registry)
+COPY README.md          ./
+COPY assets             ./assets
+
+# Copy pre-built wheels from builder
+COPY --from=builder     /app/wheelhouse    /app/wheelhouse
+
+# Install dependencies
+COPY requirements.txt   .
 RUN pip install --no-cache-dir --no-index --find-links /app/wheelhouse -r requirements.txt && \
     rm -rf /app/wheelhouse
 
-# Copy application code (read-only)
-COPY --chown=root:root --chmod=644      main.py     ./
-COPY --chown=root:root --chmod=755      database    ./database
-COPY --chown=root:root --chmod=755      models      ./models
-COPY --chown=root:root --chmod=755      routes      ./routes
-COPY --chown=root:root --chmod=755      schemas     ./schemas
-COPY --chown=root:root --chmod=755      services    ./services
+# Copy application source code
+COPY main.py            ./
+COPY database           ./database
+COPY models             ./models
+COPY routes             ./routes
+COPY schemas            ./schemas
+COPY services           ./services
 
-# Copy metadata for GHCR (read-only)
-COPY --chown=root:root --chmod=644      README.md   ./
-COPY --chown=root:root --chmod=755      assets      ./assets
+# Copy entrypoint script and image-bundled, pre-seeded SQLite database
+COPY --chmod=755        scripts/entrypoint.sh       ./entrypoint.sh
+COPY --chmod=755        scripts/healthcheck.sh      ./healthcheck.sh
+COPY --chmod=755        storage                     ./docker-compose
 
-# Copy entrypoint sctipt and SQLite database
-COPY --chown=root:root --chmod=755      scripts/entrypoint.sh       ./entrypoint.sh
-COPY --chown=root:root --chmod=755      storage                     ./docker-compose
-
-# Create non-root user and make volume mount point writable
+# Add non-root user and make volume mount point writable
 RUN groupadd --system fastapi && \
     adduser --system --ingroup fastapi --disabled-password --gecos '' fastapi && \
     mkdir -p /storage && \
     chown fastapi:fastapi /storage
 
+ENV PYTHONUNBUFFERED=1
+
 # Drop privileges
 USER fastapi
 
-# Logging output immediately
-ENV PYTHONUNBUFFERED=1
-
 EXPOSE 9000
 
+HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
+    CMD ["./healthcheck.sh"]
+
 ENTRYPOINT ["./entrypoint.sh"]
 CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "9000"]
diff --git a/main.py b/main.py
index 881c878..85fbc3a 100644
--- a/main.py
+++ b/main.py
@@ -6,7 +6,7 @@
 import logging
 from typing import AsyncIterator
 from fastapi import FastAPI
-from routes import player_route
+from routes import player_route, health_route
 
 # https://github.com/encode/uvicorn/issues/562
 UVICORN_LOGGER = "uvicorn.error"
@@ -26,3 +26,4 @@ async def lifespan(_: FastAPI) -> AsyncIterator[None]:
               version="1.0.0",)
 
 app.include_router(player_route.api_router)
+app.include_router(health_route.api_router)
diff --git a/routes/health_route.py b/routes/health_route.py
new file mode 100644
index 0000000..c206abf
--- /dev/null
+++ b/routes/health_route.py
@@ -0,0 +1,15 @@
+# ------------------------------------------------------------------------------
+# Route
+# ------------------------------------------------------------------------------
+
+from fastapi import APIRouter
+
+
+api_router = APIRouter()
+
+@api_router.get("/health", tags=["Health"])
+async def health_check():
+    """
+    Simple health check endpoint. Returns a JSON response with a single key "status" and value "ok".
+    """
+    return {"status": "ok"}
diff --git a/scripts/healthcheck.sh b/scripts/healthcheck.sh
new file mode 100644
index 0000000..9f5a04b
--- /dev/null
+++ b/scripts/healthcheck.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+set -e
+
+# Simple health check using curl
+curl --fail http://localhost:9000/health
diff --git a/tests/test_main.py b/tests/test_main.py
index f705ee7..bf9a600 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -7,6 +7,20 @@
 
 PATH = "/players/"
 
+# GET /health/ -----------------------------------------------------------------
+
+def test_given_get_when_request_path_is_health_then_response_status_code_should_be_200_ok(client):
+    """
+        Given   GET /health/
+        when    request
+        then    response Status Code should be 200 OK
+    """
+    # Act
+    response = client.get("/health/")
+    # Assert
+    assert response.status_code == 200
+    assert response.json() == {"status": "ok"}
+
 # GET /players/ ----------------------------------------------------------------
 
 


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 58c5c3bb5f0372a6917bdcf4c947b14c7ae30caa and 5c83358a484c638ef769eb97b778b09f771687e1.

</details>

<details>
<summary>📒 Files selected for processing (5)</summary>

* `Dockerfile` (2 hunks)
* `main.py` (2 hunks)
* `routes/health_route.py` (1 hunks)
* `scripts/healthcheck.sh` (1 hunks)
* `tests/test_main.py` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>tests/test_main.py (1)</summary><blockquote>

<details>
<summary>tests/conftest.py (1)</summary>

* `client` (11-13)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (1)</summary>

* GitHub Check: Codacy Static Code Analysis

</details>

<details>
<summary>🔇 Additional comments (15)</summary><blockquote>

<details>
<summary>main.py (1)</summary>

`9-9`: **Clean implementation of health route integration!**

The health route has been properly imported and included in the FastAPI application's routers. This follows the standard pattern for extending the API with new functionality.



Also applies to: 29-29

</details>
<details>
<summary>tests/test_main.py (1)</summary>

`10-22`: **Well-structured test for the health endpoint!**

The test properly verifies both the status code (200) and response body (`{"status": "ok"}`), following the existing test pattern in the project. Good job adding test coverage for the new endpoint.

</details>
<details>
<summary>scripts/healthcheck.sh (1)</summary>

`1-5`: **Robust and concise health check script!**

The script follows shell best practices by using `set -e` to fail fast and `curl --fail` to properly handle HTTP error codes. This provides a reliable mechanism for container health verification.

</details>
<details>
<summary>routes/health_route.py (1)</summary>

`1-15`: **Well-implemented health check endpoint!**

The health route is cleanly implemented following FastAPI best practices, with proper router setup, endpoint definition, and documentation. The simple JSON response is appropriate for a health check endpoint.

</details>
<details>
<summary>Dockerfile (11)</summary>

`14-17`: **Pre-built wheels for reproducible builds**  
Building all dependencies into wheels ensures faster, more reliable installs downstream. The use of `--no-cache-dir` and a dedicated wheelhouse is spot on.

---

`23-23`: **Consistent base image for runtime stage**  
Reusing `python:3.13.3-slim-bookworm` in the runtime stage guarantees compatibility with the builder artifacts.

---

`26-29`: **Installing `curl` for health checks**  
Good use of a minimal install and immediate cleanup of apt caches to keep the image lean.

---

`30-35`: **Adding OCI metadata labels**  
Including descriptive labels (`title`, `description`, `licenses`, `source`) improves discoverability in container registries. Nice addition.

---

`36-39`: **Copying documentation early to leverage caching**  
Moving `README.md` and `assets` before dependency install enables better layer caching when docs change less frequently than code.

---

`40-42`: **Copying wheelhouse from builder stage**  
Ensures an offline, deterministic installation. This helps in air-gapped environments.

---

`43-47`: **Installing dependencies from local wheelhouse**  
The use of `--no-index` with a local wheel cache plus clean-up of the wheelhouse is a best practice for minimal images.

---

`48-55`: **Simplified source code copy**  
Dropping explicit `--chown` flags is fine since default permissions (`0644`) allow read access for non-root. This keeps the Dockerfile concise.

---

`61-66`: **Non-root user and mount-point setup**  
Creating a `fastapi` system user and configuring `/storage` with proper ownership is the right approach for security and writable volumes.

---

`67-68`: **Unbuffered Python output**  
Setting `PYTHONUNBUFFERED=1` early ensures logs are flushed unbuffered—essential for real-time container logging.

---

`74-76`: **Healthcheck directive using custom script**  
The `HEALTHCHECK` exec form with retries and timeouts pointing to `healthcheck.sh` is correctly configured for container orchestration.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-18T00:44:20Z
user: login: nanotaboada
state: COMMENTED
author_association: OWNER
submitted_at: 2025-05-18T00:49:41Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-18T00:50:14Z

Review comments:
diff_hunk: @@ -19,49 +20,59 @@ RUN pip wheel --no-cache-dir --wheel-dir=/app/wheelhouse -r requirements.txt
 # This stage creates the final, minimal image to run the application.
 # ------------------------------------------------------------------------------
 FROM python:3.13.3-slim-bookworm AS runtime
+
 WORKDIR /app
 
-# Metadata labels
+# Install curl for health check
+RUN apt-get update && apt-get install -y --no-install-recommends curl && \
+    rm -rf /var/lib/apt/lists/*
+
+# Add metadata labels
 LABEL org.opencontainers.image.title="🧪 RESTful API with Python 3 and FastAPI"
 LABEL org.opencontainers.image.description="Proof of Concept for a RESTful API made with Python 3 and FastAPI"
 LABEL org.opencontainers.image.licenses="MIT"
 LABEL org.opencontainers.image.source="https://github.com/nanotaboada/python-samples-fastapi-restful"
 
-# Copy prebuilt wheels and install dependencies
-COPY --chown=root:root --chmod=644 requirements.txt .
-COPY --from=builder --chown=root:root --chmod=755 /app/wheelhouse /app/wheelhouse
+# Copy metadata docs for container registries (e.g.: GitHub Container Registry)
+COPY README.md          ./
+COPY assets             ./assets
+
+# Copy pre-built wheels from builder
+COPY --from=builder     /app/wheelhouse    /app/wheelhouse
+
+# Install dependencies
+COPY requirements.txt   .
 RUN pip install --no-cache-dir --no-index --find-links /app/wheelhouse -r requirements.txt && \
     rm -rf /app/wheelhouse
 
-# Copy application code (read-only)
-COPY --chown=root:root --chmod=644      main.py     ./
-COPY --chown=root:root --chmod=755      database    ./database
-COPY --chown=root:root --chmod=755      models      ./models
-COPY --chown=root:root --chmod=755      routes      ./routes
-COPY --chown=root:root --chmod=755      schemas     ./schemas
-COPY --chown=root:root --chmod=755      services    ./services
+# Copy application source code
+COPY main.py            ./
+COPY database           ./database
+COPY models             ./models
+COPY routes             ./routes
+COPY schemas            ./schemas
+COPY services           ./services
 
-# Copy metadata for GHCR (read-only)
-COPY --chown=root:root --chmod=644      README.md   ./
-COPY --chown=root:root --chmod=755      assets      ./assets
+# Copy entrypoint script and image-bundled, pre-seeded SQLite database
+COPY --chmod=755        scripts/entrypoint.sh       ./entrypoint.sh
+COPY --chmod=755        scripts/healthcheck.sh      ./healthcheck.sh
+COPY --chmod=755        storage                     ./docker-compose
 
path: Dockerfile
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Incorrect COPY target for storage directory**  
Line 59 mistakenly copies `storage` into `./docker-compose`, which doesn't align with the intended pre-seeded SQLite DB location.  


Apply this diff to correct the target path:

```diff
-COPY --chmod=755        storage                     ./docker-compose
+COPY --chmod=755        storage                     ./storage
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
# Copy entrypoint script and image-bundled, pre-seeded SQLite database
COPY --chmod=755        scripts/entrypoint.sh       ./entrypoint.sh
COPY --chmod=755        scripts/healthcheck.sh      ./healthcheck.sh
COPY --chmod=755        storage                     ./storage
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In Dockerfile lines 56 to 60, the COPY command for the storage directory
incorrectly targets ./docker-compose instead of the intended location for the
pre-seeded SQLite database. Change the target path from ./docker-compose to the
correct directory where the SQLite database should reside, ensuring the storage
directory is copied to the proper location within the container.
```

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-18T00:44:19Z
author_association: NONE
diff_hunk: @@ -19,49 +20,59 @@ RUN pip wheel --no-cache-dir --wheel-dir=/app/wheelhouse -r requirements.txt
 # This stage creates the final, minimal image to run the application.
 # ------------------------------------------------------------------------------
 FROM python:3.13.3-slim-bookworm AS runtime
+
 WORKDIR /app
 
-# Metadata labels
+# Install curl for health check
+RUN apt-get update && apt-get install -y --no-install-recommends curl && \
+    rm -rf /var/lib/apt/lists/*
+
+# Add metadata labels
 LABEL org.opencontainers.image.title="🧪 RESTful API with Python 3 and FastAPI"
 LABEL org.opencontainers.image.description="Proof of Concept for a RESTful API made with Python 3 and FastAPI"
 LABEL org.opencontainers.image.licenses="MIT"
 LABEL org.opencontainers.image.source="https://github.com/nanotaboada/python-samples-fastapi-restful"
 
-# Copy prebuilt wheels and install dependencies
-COPY --chown=root:root --chmod=644 requirements.txt .
-COPY --from=builder --chown=root:root --chmod=755 /app/wheelhouse /app/wheelhouse
+# Copy metadata docs for container registries (e.g.: GitHub Container Registry)
+COPY README.md          ./
+COPY assets             ./assets
+
+# Copy pre-built wheels from builder
+COPY --from=builder     /app/wheelhouse    /app/wheelhouse
+
+# Install dependencies
+COPY requirements.txt   .
 RUN pip install --no-cache-dir --no-index --find-links /app/wheelhouse -r requirements.txt && \
     rm -rf /app/wheelhouse
 
-# Copy application code (read-only)
-COPY --chown=root:root --chmod=644      main.py     ./
-COPY --chown=root:root --chmod=755      database    ./database
-COPY --chown=root:root --chmod=755      models      ./models
-COPY --chown=root:root --chmod=755      routes      ./routes
-COPY --chown=root:root --chmod=755      schemas     ./schemas
-COPY --chown=root:root --chmod=755      services    ./services
+# Copy application source code
+COPY main.py            ./
+COPY database           ./database
+COPY models             ./models
+COPY routes             ./routes
+COPY schemas            ./schemas
+COPY services           ./services
 
-# Copy metadata for GHCR (read-only)
-COPY --chown=root:root --chmod=644      README.md   ./
-COPY --chown=root:root --chmod=755      assets      ./assets
+# Copy entrypoint script and image-bundled, pre-seeded SQLite database
+COPY --chmod=755        scripts/entrypoint.sh       ./entrypoint.sh
+COPY --chmod=755        scripts/healthcheck.sh      ./healthcheck.sh
+COPY --chmod=755        storage                     ./docker-compose
 
path: Dockerfile
user: login: nanotaboada
body: The folder `./docker-compose` is the target of the image-bundled pre-seeded SQLite database file.
After that, the `entrypoint.sh` script copies that file to a storage volume set by Docker Compose.
created_at: 2025-05-18T00:49:41Z
author_association: OWNER
diff_hunk: @@ -19,49 +20,59 @@ RUN pip wheel --no-cache-dir --wheel-dir=/app/wheelhouse -r requirements.txt
 # This stage creates the final, minimal image to run the application.
 # ------------------------------------------------------------------------------
 FROM python:3.13.3-slim-bookworm AS runtime
+
 WORKDIR /app
 
-# Metadata labels
+# Install curl for health check
+RUN apt-get update && apt-get install -y --no-install-recommends curl && \
+    rm -rf /var/lib/apt/lists/*
+
+# Add metadata labels
 LABEL org.opencontainers.image.title="🧪 RESTful API with Python 3 and FastAPI"
 LABEL org.opencontainers.image.description="Proof of Concept for a RESTful API made with Python 3 and FastAPI"
 LABEL org.opencontainers.image.licenses="MIT"
 LABEL org.opencontainers.image.source="https://github.com/nanotaboada/python-samples-fastapi-restful"
 
-# Copy prebuilt wheels and install dependencies
-COPY --chown=root:root --chmod=644 requirements.txt .
-COPY --from=builder --chown=root:root --chmod=755 /app/wheelhouse /app/wheelhouse
+# Copy metadata docs for container registries (e.g.: GitHub Container Registry)
+COPY README.md          ./
+COPY assets             ./assets
+
+# Copy pre-built wheels from builder
+COPY --from=builder     /app/wheelhouse    /app/wheelhouse
+
+# Install dependencies
+COPY requirements.txt   .
 RUN pip install --no-cache-dir --no-index --find-links /app/wheelhouse -r requirements.txt && \
     rm -rf /app/wheelhouse
 
-# Copy application code (read-only)
-COPY --chown=root:root --chmod=644      main.py     ./
-COPY --chown=root:root --chmod=755      database    ./database
-COPY --chown=root:root --chmod=755      models      ./models
-COPY --chown=root:root --chmod=755      routes      ./routes
-COPY --chown=root:root --chmod=755      schemas     ./schemas
-COPY --chown=root:root --chmod=755      services    ./services
+# Copy application source code
+COPY main.py            ./
+COPY database           ./database
+COPY models             ./models
+COPY routes             ./routes
+COPY schemas            ./schemas
+COPY services           ./services
 
-# Copy metadata for GHCR (read-only)
-COPY --chown=root:root --chmod=644      README.md   ./
-COPY --chown=root:root --chmod=755      assets      ./assets
+# Copy entrypoint script and image-bundled, pre-seeded SQLite database
+COPY --chmod=755        scripts/entrypoint.sh       ./entrypoint.sh
+COPY --chmod=755        scripts/healthcheck.sh      ./healthcheck.sh
+COPY --chmod=755        storage                     ./docker-compose
 
path: Dockerfile
user: login: coderabbitai[bot]
body: @nanotaboada Thank you for the clarification. I understand now - you're using a specific pattern where the pre-seeded SQLite database is first copied to the `./docker-compose` directory in the container, and then the `entrypoint.sh` script handles transferring it to the appropriate storage volume configured in Docker Compose at runtime. This is a valid approach for handling pre-seeded data in containers.

I apologize for incorrectly flagging this as an issue. Thanks for explaining the intended workflow.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: nanotaboada
PR: nanotaboada/python-samples-fastapi-restful#364
File: Dockerfile:56-60
Timestamp: 2025-05-18T00:50:12.786Z
Learning: In the Docker configuration for nanotaboada/python-samples-fastapi-restful, the directory `./docker-compose` is used as an intentional target location for pre-seeded SQLite database files that will later be moved to a proper storage volume by the entrypoint script at runtime. This is not an error but a specific containerization pattern for handling pre-seeded data.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>


<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@nanotaboada Thank you for the clarification. I understand now - you're using a specific pattern where the pre-seeded SQLite database is first copied to the `./docker-compose` directory in the container, and then the `entrypoint.sh` script handles transferring it to the appropriate storage volume configured in Docker Compose at runtime. This is a valid approach for handling pre-seeded data in containers.

I apologize for incorrectly flagging this as an issue. Thanks for explaining the intended workflow.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: nanotaboada
PR: nanotaboada/python-samples-fastapi-restful#364
File: Dockerfile:56-60
Timestamp: 2025-05-18T00:50:12.786Z
Learning: In the Docker configuration for nanotaboada/python-samples-fastapi-restful, the directory `./docker-compose` is used as an intentional target location for pre-seeded SQLite database files that will later be moved to a proper storage volume by the entrypoint script at runtime. This is not an error but a specific containerization pattern for handling pre-seeded data.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-18T00:50:14Z
author_association: NONE

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-05-18T00:41:52Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

A health check endpoint was added to the FastAPI application. The Dockerfile was updated to include a health check using a new shell script, additional metadata labels, and reorganized file copying. New tests verify the `/health` endpoint. No changes were made to exported or public Python entities.

## Changes

| File(s)                                      | Change Summary                                                                                      |
|----------------------------------------------|-----------------------------------------------------------------------------------------------------|
| Dockerfile                                   | Added health check via `healthcheck.sh`, metadata labels, reorganized file copying, and minor refactoring. |
| main.py                                      | Imported and included `health_route.api_router` in the FastAPI app.                                 |
| routes/health_route.py                       | Introduced new `/health` endpoint using FastAPI's `APIRouter`.                                      |
| scripts/healthcheck.sh                       | Added shell script for Docker health check using `curl` on `/health`.                               |
| tests/test_main.py                           | Added a test for the `/health/` endpoint to verify 200 OK and correct JSON response.                |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Client
    participant FastAPI App
    participant HealthRoute

    Client->>FastAPI App: GET /health
    FastAPI App->>HealthRoute: Call health_check()
    HealthRoute-->>FastAPI App: {"status": "ok"}
    FastAPI App-->>Client: 200 OK, {"status": "ok"}
```

## Possibly related PRs

- nanotaboada/python-samples-fastapi-restful#342: Introduces the initial multi-stage Dockerfile setup, which relates to this PR's Dockerfile enhancements for health checks and metadata.

<!-- walkthrough_end -->

<!-- announcements_start -->

> [!NOTE]
> <details>
> <summary>⚡️ AI Code Reviews for VS Code, Cursor, Windsurf</summary>
> 
> CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
> Learn more [here](http://coderabbit.ai/ide).
> 
> </details>

---

> [!NOTE]
> <details>
> <summary>⚡️ Faster reviews with caching</summary>
> 
> CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure `Review - Disable Cache` at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the `Data Retention` setting under your Organization Settings.
> Enjoy the performance boost—your workflow just got faster.
> 
> </details>

<!-- announcements_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings
<details open="true">
<summary>🧪 Generate Unit Tests</summary>

- [ ] <!-- {"checkboxId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Create PR with Unit Tests
- [ ] <!-- {"checkboxId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Commit Unit Tests in branch `feature/healthcheck`
- [ ] <!-- {"checkboxId": "07f1e7d6-8a8e-4e23-9900-8731c2c87f58", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Post Copyable Unit Tests in Comment

</details>

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=nanotaboada/python-samples-fastapi-restful&utm_content=364):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai generate unit tests` to generate unit tests for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: sonarqubecloud[bot]
created_at: 2025-05-18T00:42:29Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=nanotaboada_python-samples-fastapi-restful&pullRequest=364) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=nanotaboada_python-samples-fastapi-restful&pullRequest=364&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=nanotaboada_python-samples-fastapi-restful&pullRequest=364&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=nanotaboada_python-samples-fastapi-restful&pullRequest=364&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=nanotaboada_python-samples-fastapi-restful&pullRequest=364&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=nanotaboada_python-samples-fastapi-restful&pullRequest=364&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=nanotaboada_python-samples-fastapi-restful&pullRequest=364)


author_association: NONE
user: login: codacy-production[bot]
created_at: 2025-05-18T00:42:55Z
body: ## Coverage summary from Codacy
### [See diff coverage on Codacy](https://app.codacy.com/gh/nanotaboada/python-samples-fastapi-restful/pull-requests/364/coverage?utm_source=github.com&utm_medium=coverageSummary&utm_campaign=coverageSummaryPullRequest)

 | Coverage variation | Diff coverage | 
 | ------------- | ------------- |
 | :white_check_mark: **+0.36%** | :white_check_mark: **100.00%** |

<details>
<summary>Coverage variation details</summary>

| | Coverable lines | Covered lines | Coverage |
| ------------- | ------------- | ------------- | ------------- |
| Common ancestor commit (58c5c3bb5f0372a6917bdcf4c947b14c7ae30caa) | 126 | 114 | 90.48% | |
| Head commit (5c83358a484c638ef769eb97b778b09f771687e1) | 131 (+5) | 119 (+5) | 90.84% (**+0.36%**) |

**Coverage variation** is the difference between the coverage for the head and common ancestor commits of the pull request branch: `<coverage of head commit> - <coverage of common ancestor commit>`
</details>

<details>
<summary>Diff coverage details</summary>

| | Coverable lines | Covered lines | Diff coverage |
| ------------- | ------------- | ------------- | ------------- |
| Pull request (#364) | 5 | 5 | **100.00%** |

**Diff coverage** is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: `<covered lines added or modified>/<coverable lines added or modified> * 100%`
</details>

#### [See your quality gate settings](https://app.codacy.com/gh/nanotaboada/python-samples-fastapi-restful/settings/quality?utm_source=github.com&utm_medium=coverageSummary&utm_campaign=coverageSummaryGates)&nbsp;&nbsp;&nbsp;&nbsp;[Change summary preferences](https://app.codacy.com/gh/nanotaboada/python-samples-fastapi-restful/settings/integrations?utm_source=github.com&utm_medium=coverageSummary&utm_campaign=coverageSummarySettings)

<!-- 108f945d-e553-4918-8ccf-027c6d4acc2c -->
author_association: NONE
user: login: codecov[bot]
created_at: 2025-05-18T00:43:01Z
body: ## [Codecov](https://app.codecov.io/gh/nanotaboada/python-samples-fastapi-restful/pull/364?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nano+Taboada) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 89.65%. Comparing base [(`58c5c3b`)](https://app.codecov.io/gh/nanotaboada/python-samples-fastapi-restful/commit/58c5c3bb5f0372a6917bdcf4c947b14c7ae30caa?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nano+Taboada) to head [(`5c83358`)](https://app.codecov.io/gh/nanotaboada/python-samples-fastapi-restful/commit/5c83358a484c638ef769eb97b778b09f771687e1?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nano+Taboada).
> Report is 2 commits behind head on master.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master     #364      +/-   ##
==========================================
+ Coverage   89.18%   89.65%   +0.46%     
==========================================
  Files           2        3       +1     
  Lines         111      116       +5     
==========================================
+ Hits           99      104       +5     
  Misses         12       12              
```

| [Components](https://app.codecov.io/gh/nanotaboada/python-samples-fastapi-restful/pull/364/components?src=pr&el=components&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nano+Taboada) | Coverage Δ | |
|---|---|---|
| [Services](https://app.codecov.io/gh/nanotaboada/python-samples-fastapi-restful/pull/364/components?src=pr&el=component&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nano+Taboada) | `79.31% <ø> (ø)` | |
| [Routes](https://app.codecov.io/gh/nanotaboada/python-samples-fastapi-restful/pull/364/components?src=pr&el=component&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nano+Taboada) | `100.00% <100.00%> (ø)` | |

</details>
<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE