DEV Community

Daniel Popoola
Daniel Popoola

Posted on

Building a Health-Check Microservice with FastAPI

In modern application development, health checks play a crucial role in ensuring reliability, observability, and smooth orchestrationโ€”especially in containerized environments like Docker or Kubernetes. In this post, Iโ€™ll walk you through how I built a production-ready health-check microservice using FastAPI.

This project features structured logging, clean separation of concerns, and asynchronous service checks for both a database and Redisโ€”all built in a modular and extensible way.

GitHub Repo: [https://github.com/DanielPopoola/fastapi-microservice-health-check]


๐Ÿš€ What This Project Covers

  • Creating a /health/ endpoint with real service checks (DB, Redis)
  • Supporting /live and /ready endpoints for Kubernetes probes
  • Using async asyncio.gather() for fast, parallel checks
  • Configurable settings with Pydantic
  • Structured logging with custom log formatting using loguru.
  • Middleware for request timing and error handling

๐Ÿ“ Project Structure

project/
โ”œโ”€โ”€ main.py             # App factory and configuration
โ”œโ”€โ”€ config.py           # App settings via Pydantic
โ”œโ”€โ”€ routers/
โ”‚   โ”œโ”€โ”€ health.py       # Health check endpoints
โ”‚   โ””โ”€โ”€ echo.py         # Echo endpoint (for demo)
โ”œโ”€โ”€ utils/
โ”‚   โ””โ”€โ”€ logging.py      # Custom logger setup
โ””โ”€โ”€ ...
Enter fullscreen mode Exit fullscreen mode

๐Ÿ” Under the Hood: main.py

main.py acts as the orchestrator. Here's what it handles:

1. App Lifecycle Management

@asynccontextmanager
async def lifespan(app: FastAPI):
    logger.info("Application starting up")
    yield
    logger.info("Application shutting down")
Enter fullscreen mode Exit fullscreen mode

This cleanly logs startup and shutdown events, essential for container lifecycle awareness.

2. App Factory Pattern

The create_app() function encapsulates app setup:

  • Loads settings with get_settings()
  • Sets up structured logging
  • Registers CORS middleware
  • Adds global and HTTP exception handlers
  • Includes routers for modularity

3. Middleware

A custom middleware logs request data and execution time:

@app.middleware("http")
async def log_requests(request, call_next):
    start_time = time.time()
    response = await call_next(request)
    response.headers["X-Response-Time"] = f"{(time.time() - start_time) * 1000:.2f}ms"
    return response
Enter fullscreen mode Exit fullscreen mode

4. Exception Handling

Two global handlers catch errors and format them consistently:

  • One for HTTPException
  • One for unexpected Exception

โš•๏ธ Health Check Logic (routers/health.py)

The routers/health.py file houses the core of this service:

โœ… /health/

Performs parallel health checks using asyncio.gather():

async def perform_health_checks(settings: Settings) -> Dict[str, ServiceCheck]:
    checks = {}
    tasks = []
    if settings.database_url:
        tasks.append(("database", check_database(settings.database_url, settings.health_check_timeout)))  
    if settings.redis_url:
        tasks.append(("redis", check_redis(settings.redis_url, settings.health_check_timeout)))
    if tasks:
        results = await asyncio.gather(*[task[1] for task in tasks], return_exceptions=True)
        ...
    return checks


Enter fullscreen mode Exit fullscreen mode

The result is a combined status response showing the health of each component.

๐Ÿ” /live

A simple liveness check returning HTTP 200 to signal the app is alive.

๐Ÿ“ฆ /ready

Waits for both Redis and DB to pass checks before returning 200. Useful for Kubernetes readiness probes.


๐Ÿ“ก Root Endpoint and Echo

  • / returns app metadata like name, version, and timestamp
  • /echo is a simple test endpoint to verify connectivity

๐Ÿ› ๏ธ How to Run It

uvicorn app.main:app --reload
Enter fullscreen mode Exit fullscreen mode

Or using the embedded __main__ block:

python -m main
Enter fullscreen mode Exit fullscreen mode

๐ŸŒŸ Whatโ€™s Next?

  • Add more service checks (e.g., external APIs, caches)
  • Integrate with Dockerโ€™s HEALTHCHECK instruction
  • Configure Kubernetes readiness/liveness probes

๐Ÿง  Final Thoughts

Building robust health checks is one of the simplest yet most impactful ways to improve system reliability. With FastAPIโ€™s speed and async support, this project offers a solid base for both simple and enterprise-grade applications.

GitHub: DanielPopoola/fastapi-microservice-health-check

Top comments (0)