Fix health check endpoint and server configuration for port 8001

This commit is contained in:
Automated Action 2025-05-19 09:22:13 +00:00
parent 657900b91a
commit e228630abf
21 changed files with 110 additions and 57 deletions

View File

@ -50,15 +50,24 @@ pip install -r requirements.txt
3. Run the application: 3. Run the application:
```bash ```bash
uvicorn main:app --reload uvicorn main:app --host 0.0.0.0 --port 8001 --reload
``` ```
The API will be available at http://localhost:8000. The API will be available at http://localhost:8001.
### Health Check
The application has a dedicated health check endpoint at `/health` that returns a simple JSON response:
```json
{"status": "ok"}
```
This endpoint can be used by load balancers, container orchestrators, or monitoring tools to verify that the application is running correctly.
## API Documentation ## API Documentation
- Swagger UI: http://localhost:8000/docs - Swagger UI: http://localhost:8001/docs
- ReDoc: http://localhost:8000/redoc - ReDoc: http://localhost:8001/redoc
## API Endpoints ## API Endpoints

View File

@ -1,7 +1,6 @@
from fastapi import APIRouter from fastapi import APIRouter
from app.api import health, items from app.api import items
api_router = APIRouter() api_router = APIRouter()
api_router.include_router(health.router, tags=["health"])
api_router.include_router(items.router, prefix="/items", tags=["items"]) api_router.include_router(items.router, prefix="/items", tags=["items"])

View File

@ -1,11 +1,12 @@
from fastapi import APIRouter from fastapi import APIRouter, status
router = APIRouter() router = APIRouter()
@router.get("/health", tags=["health"]) @router.get("/health", tags=["health"], status_code=status.HTTP_200_OK)
async def health_check(): async def health_check():
""" """
Health check endpoint Health check endpoint that returns OK status.
This endpoint is used to verify the application is running correctly.
""" """
return {"status": "ok"} return {"status": "ok"}

View File

@ -8,6 +8,10 @@ class Settings(BaseSettings):
API_V1_STR: str = "/api/v1" API_V1_STR: str = "/api/v1"
PROJECT_NAME: str = "Generic REST API Service" PROJECT_NAME: str = "Generic REST API Service"
# Server Configuration
HOST: str = "0.0.0.0"
PORT: int = 8001
# CORS Configuration # CORS Configuration
BACKEND_CORS_ORIGINS: List[AnyHttpUrl] = [] BACKEND_CORS_ORIGINS: List[AnyHttpUrl] = []

View File

@ -43,7 +43,7 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
db: Session, db: Session,
*, *,
db_obj: ModelType, db_obj: ModelType,
obj_in: Union[UpdateSchemaType, Dict[str, Any]] obj_in: Union[UpdateSchemaType, Dict[str, Any]],
) -> ModelType: ) -> ModelType:
obj_data = jsonable_encoder(db_obj) obj_data = jsonable_encoder(db_obj)
if isinstance(obj_in, dict): if isinstance(obj_in, dict):

View File

@ -4,8 +4,7 @@ from sqlalchemy.orm import sessionmaker
from app.core.config import settings from app.core.config import settings
engine = create_engine( engine = create_engine(
settings.SQLALCHEMY_DATABASE_URL, settings.SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
connect_args={"check_same_thread": False}
) )
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

View File

@ -11,4 +11,6 @@ class Item(Base):
description = Column(Text, nullable=True) description = Column(Text, nullable=True)
is_active = Column(Boolean, default=True) is_active = Column(Boolean, default=True)
created_at = Column(DateTime(timezone=True), server_default=func.now()) created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) updated_at = Column(
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
)

19
main.py
View File

@ -1,12 +1,11 @@
from fastapi import FastAPI from fastapi import FastAPI, status
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from app.api.base import api_router from app.api.base import api_router
from app.core.config import settings from app.core.config import settings
app = FastAPI( app = FastAPI(
title=settings.PROJECT_NAME, title=settings.PROJECT_NAME, openapi_url=f"{settings.API_V1_STR}/openapi.json"
openapi_url=f"{settings.API_V1_STR}/openapi.json"
) )
# Set all CORS enabled origins # Set all CORS enabled origins
@ -19,17 +18,21 @@ if settings.BACKEND_CORS_ORIGINS:
allow_headers=["*"], allow_headers=["*"],
) )
app.include_router(api_router, prefix=settings.API_V1_STR)
# Add root health check endpoint
@app.get("/health", tags=["health"]) @app.get("/health", tags=["health"], status_code=status.HTTP_200_OK)
async def health_check(): async def health_check():
""" """
Health check endpoint Root health check endpoint.
This endpoint is used for server health monitoring.
""" """
return {"status": "ok"} return {"status": "ok"}
# Include API routes
app.include_router(api_router, prefix=settings.API_V1_STR)
if __name__ == "__main__": if __name__ == "__main__":
import uvicorn import uvicorn
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
uvicorn.run("main:app", host=settings.HOST, port=settings.PORT, reload=True)

View File

@ -64,7 +64,7 @@ def run_migrations_online() -> None:
) )
with connectable.connect() as connection: with connectable.connect() as connection:
is_sqlite = connection.dialect.name == 'sqlite' is_sqlite = connection.dialect.name == "sqlite"
context.configure( context.configure(
connection=connection, connection=connection,
target_metadata=target_metadata, target_metadata=target_metadata,

View File

@ -5,12 +5,13 @@ Revises:
Create Date: 2023-10-01 10:00:00.000000 Create Date: 2023-10-01 10:00:00.000000
""" """
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = '01234567890a' revision = "01234567890a"
down_revision = None down_revision = None
branch_labels = None branch_labels = None
depends_on = None depends_on = None
@ -18,23 +19,34 @@ depends_on = None
def upgrade() -> None: def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
op.create_table('item', op.create_table(
sa.Column('id', sa.Integer(), nullable=False), "item",
sa.Column('title', sa.String(length=255), nullable=False), sa.Column("id", sa.Integer(), nullable=False),
sa.Column('description', sa.Text(), nullable=True), sa.Column("title", sa.String(length=255), nullable=False),
sa.Column('is_active', sa.Boolean(), nullable=True), sa.Column("description", sa.Text(), nullable=True),
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=True), sa.Column("is_active", sa.Boolean(), nullable=True),
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=True), sa.Column(
sa.PrimaryKeyConstraint('id') "created_at",
sa.DateTime(timezone=True),
server_default=sa.text("(CURRENT_TIMESTAMP)"),
nullable=True,
),
sa.Column(
"updated_at",
sa.DateTime(timezone=True),
server_default=sa.text("(CURRENT_TIMESTAMP)"),
nullable=True,
),
sa.PrimaryKeyConstraint("id"),
) )
op.create_index(op.f('ix_item_id'), 'item', ['id'], unique=False) op.create_index(op.f("ix_item_id"), "item", ["id"], unique=False)
op.create_index(op.f('ix_item_title'), 'item', ['title'], unique=False) op.create_index(op.f("ix_item_title"), "item", ["title"], unique=False)
# ### end Alembic commands ### # ### end Alembic commands ###
def downgrade() -> None: def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f('ix_item_title'), table_name='item') op.drop_index(op.f("ix_item_title"), table_name="item")
op.drop_index(op.f('ix_item_id'), table_name='item') op.drop_index(op.f("ix_item_id"), table_name="item")
op.drop_table('item') op.drop_table("item")
# ### end Alembic commands ### # ### end Alembic commands ###

View File

@ -5,4 +5,6 @@ alembic>=1.12.0,<1.13.0
pydantic>=2.3.0,<2.4.0 pydantic>=2.3.0,<2.4.0
pydantic-settings>=2.0.3,<2.1.0 pydantic-settings>=2.0.3,<2.1.0
python-dotenv>=1.0.0,<1.1.0 python-dotenv>=1.0.0,<1.1.0
httpx>=0.24.1,<0.25.0 # Required for TestClient
pytest>=7.4.0,<7.5.0
ruff>=0.0.287,<0.1.0 ruff>=0.0.287,<0.1.0

15
test_health.py Normal file
View File

@ -0,0 +1,15 @@
from fastapi.testclient import TestClient
from main import app
client = TestClient(app)
def test_health_endpoint():
response = client.get("/health")
assert response.status_code == 200
assert response.json() == {"status": "ok"}
if __name__ == "__main__":
test_result = test_health_endpoint()
print("Health endpoint test passed!")

7
test_health_endpoint.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
echo "Testing health endpoint on port 8001..."
echo "GET /health HTTP/1.1"
echo "Expected response: {'status': 'ok'}"
echo ""
echo "This script will help you verify that the health endpoint is working correctly."
echo "Run this script after starting the application with 'uvicorn main:app --host 0.0.0.0 --port 8001'"