Skip to content

refactor: todo app#912

Open
yxtay wants to merge 6 commits intomainfrom
refactor/todo-app
Open

refactor: todo app#912
yxtay wants to merge 6 commits intomainfrom
refactor/todo-app

Conversation

@yxtay
Copy link
Owner

@yxtay yxtay commented Feb 27, 2026

No description provided.

- Create layered architecture with 7 core modules (routes, service, repository, database, entities, schemas, dependencies)
- Implement repository pattern with abstract interface and concrete implementation
- Add comprehensive dependency injection chain (Session → Repository → Service → Routes)
- Rename all 'todo' references to 'task' throughout codebase (API routes, classes, functions, parameters)
- Update API routes from /todos to /tasks prefix
- Add 11 comprehensive test cases with 85% code coverage
- Implement database layer with SQLModel ORM and SQLite backend
- Add health check endpoints and lifespan context manager
- Create ARCHITECTURE.md and QUICKSTART.md documentation
- Fix pre-commit markdown linting issues (MD013 line length, MD040 code block language specs)
- Update .gitignore to exclude data/ directory and *.db files
Replace Gunicorn-based entrypoints with FastAPI run invocations across the project. Updated Dockerfile dev/prod CMDs to use `fastapi run src/example_app/main.py` (with `--reload` in dev) and adjusted Makefile targets (`run-dev` and `run`) to invoke `uv run fastapi run src/${APP_NAME}/main.py` (dev includes `--reload`). This switches the container and local run flow to the FastAPI runner for direct ASGI execution and enables live reload during development.
Refactor routing: introduce a routes package and move health endpoints into src/example_app/routes/health.py. Tasks endpoints were moved to src/example_app/routes/tasks.py and exported via src/example_app/routes/__init__.py (health_router, tasks_router). Update imports in main and other modules to use the new routers, remove the inline health endpoints from main, and delete the standalone models.Ok (src/example_app/models.py). Also adjust logger typing (get_logger -> logger.__class__) and update tests to hit /healthz.
@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant refactoring of the Todo application, transforming it into a more robust and maintainable FastAPI service. The core objective was to establish a clean architectural pattern, leveraging dependency injection and a repository pattern to enhance modularity, testability, and scalability. This change provides a clear structure for future development, making the codebase easier to understand and extend, while also simplifying the local development and deployment process.

Highlights

  • Architectural Refactor: The application has been refactored to implement a clean layered architecture, including dedicated layers for API routes, business logic (service), data access (repository), and database management, promoting separation of concerns and testability.
  • Dependency Injection (DI): Comprehensive dependency injection has been introduced using FastAPI's DI system, facilitating loose coupling between layers and simplifying testing by allowing easy mocking of dependencies.
  • Database Integration: A new database layer has been added using SQLModel and SQLite, complete with entity definitions, session management, and automatic table creation on application startup.
  • Gunicorn Removal and Simplified Deployment: All Gunicorn-related files and dependencies have been removed, streamlining the application's deployment and execution by directly using fastapi run or uvicorn.
  • Enhanced Documentation and Testing: New ARCHITECTURE.md and QUICKSTART.md files provide detailed insights into the application's design and usage. The test suite has been significantly expanded to cover the new API functionality.
Changelog
  • .gitignore
    • Added entries to ignore database files and the 'data/' directory.
  • ARCHITECTURE.md
    • Added a new file detailing the layered architecture, key components (schemas, entities, repository, service, routes, dependencies, database), dependency injection flow, API endpoints, running instructions, testing, best practices, project structure, and benefits of the new design.
  • Dockerfile
    • Updated the CMD instruction to use fastapi run instead of gunicorn for both development and production stages.
  • Makefile
    • Modified run-dev and run commands to utilize uv run fastapi run for starting the application, replacing the Gunicorn-based commands.
  • QUICKSTART.md
    • Added a new file providing a quick start guide for the Task API, including an overview, running instructions, API documentation links, example curl commands for CRUD operations, testing instructions, project structure, and architecture highlights.
  • pyproject.toml
    • Removed the gunicorn dependency from the project configuration.
  • src/example_app/database.py
    • Added a new file responsible for database configuration, creating SQLModel entities, managing database sessions, and creating tables on startup.
  • src/example_app/dependencies.py
    • Added a new file to define FastAPI dependency injection functions for providing database sessions, task repositories, and task services to routes.
  • src/example_app/entities.py
    • Added a new file defining the Task SQLModel entity, including fields for ID, title, description, completion status, and timestamps.
  • src/example_app/gunicorn.py
    • Removed the Gunicorn application setup file.
  • src/example_app/gunicorn_conf.py
    • Removed the Gunicorn configuration file.
  • src/example_app/logger.py
    • Updated the type hint for get_logger to logger.__class__ and removed an unused loguru import.
  • src/example_app/main.py
    • Refactored the main application file to include an asynccontextmanager for application lifespan (startup/shutdown), which now handles database table creation.
    • Updated the fastapi_app function to configure the FastAPI instance with a title, description, version, and the new lifespan manager.
    • Replaced direct health check endpoints with inclusion of new health_router and tasks_router.
  • src/example_app/models.py
    • Removed the Ok Pydantic model.
  • src/example_app/repository.py
    • Added a new file defining AbstractTaskRepository and its concrete implementation TaskRepository, which encapsulates all CRUD operations for tasks using SQLModel.
  • src/example_app/routes.py
    • Removed this file, as its functionality was split into src/example_app/routes/health.py and src/example_app/routes/tasks.py.
  • src/example_app/routes/init.py
    • Added a new __init__.py file to import and expose the health_router and tasks_router for modular route management.
  • src/example_app/routes/health.py
    • Added a new file defining dedicated health check endpoints (/healthz, /readyz, /livez) using a separate APIRouter.
  • src/example_app/routes/tasks.py
    • Added a new file defining all API endpoints for task operations (create, list, get, update, delete, mark complete/incomplete) using a dedicated APIRouter and integrating with the service layer.
  • src/example_app/schemas.py
    • Added a new file defining Pydantic schemas for TaskBase, TaskCreate, TaskUpdate, TaskResponse, TaskListResponse, and a generic MessageResponse.
  • src/example_app/service.py
    • Added a new file implementing the TaskService, which contains the business logic for task operations and interacts with the AbstractTaskRepository.
  • tests/test_app.py
    • Significantly expanded the test suite to include pytest fixtures for database sessions and test clients.
    • Added comprehensive tests for application creation, health checks, and all CRUD operations (create, list, get, update, delete, mark complete/incomplete) for tasks.
    • Added tests for filtering tasks by completion status.
  • tests/test_gunicorn.py
    • Removed the test file related to Gunicorn functionality.
  • uv.lock
    • Removed the gunicorn package from the locked dependencies.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 27, 2026

kics-logo

KICS version: v2.1.19

Category Results
CRITICAL CRITICAL 0
HIGH HIGH 0
MEDIUM MEDIUM 4
LOW LOW 0
INFO INFO 0
TRACE TRACE 0
TOTAL TOTAL 4
Metric Values
Files scanned placeholder 7
Files parsed placeholder 7
Files failed to scan placeholder 0
Total executed queries placeholder 73
Queries failed to execute placeholder 0
Execution time placeholder 1

Queries Results

Query Name Query Id Severity Platform Cwe Risk Score Category Experimental Description File Name Line Issue Type Search Key Expected Value Actual Value
Apt Get Install Pin Version Not Defined 965a08d7-ef86-4f14-8792-4a3b2098937e MEDIUM Dockerfile 1357 5.7 Supply-Chain false When installing a package, its pin version should be defined Dockerfile 94 MissingAttribute FROM={{dev AS compile}}.RUN={{apt-get update && apt-get install --yes --no-install-recommends binutils patchelf && rm -rf /var/lib/apt/lists/*}} Package 'binutils' has version defined Package 'binutils' does not have version defined
Apt Get Install Pin Version Not Defined 965a08d7-ef86-4f14-8792-4a3b2098937e MEDIUM Dockerfile 1357 5.7 Supply-Chain false When installing a package, its pin version should be defined Dockerfile 46 MissingAttribute FROM={{base AS dev}}.RUN={{apt-get update && apt-get install --yes --no-install-recommends build-essential && rm -rf /var/lib/apt/lists/*}} Package 'build-essential' has version defined Package 'build-essential' does not have version defined
Apt Get Install Pin Version Not Defined 965a08d7-ef86-4f14-8792-4a3b2098937e MEDIUM Dockerfile 1357 5.7 Supply-Chain false When installing a package, its pin version should be defined Dockerfile 36 MissingAttribute FROM={{debian:stable-slim@sha256:85dfcffff3c1e193877f143d05eaba8ae7f3f95cb0a32e0bc04a448077e1ac69 AS base}}.RUN={{apt-get update && apt-get upgrade --yes && apt-get install --yes --no-install-recommends curl && rm -rf /var/lib/apt/lists/*}} Package 'curl' has version defined Package 'curl' does not have version defined
Apt Get Install Pin Version Not Defined 965a08d7-ef86-4f14-8792-4a3b2098937e MEDIUM Dockerfile 1357 5.7 Supply-Chain false When installing a package, its pin version should be defined Dockerfile 94 MissingAttribute FROM={{dev AS compile}}.RUN={{apt-get update && apt-get install --yes --no-install-recommends binutils patchelf && rm -rf /var/lib/apt/lists/*}} Package 'patchelf' has version defined Package 'patchelf' does not have version defined

@github-actions
Copy link
Contributor

github-actions bot commented Feb 27, 2026

⚠️MegaLinter analysis: Success with warnings

Descriptor Linter Files Fixed Errors Warnings Elapsed time
✅ COPYPASTE jscpd yes no no 1.36s
✅ DOCKERFILE hadolint 1 0 0 0.43s
✅ EDITORCONFIG editorconfig-checker 28 0 0 0.31s
✅ MARKDOWN markdownlint 2 0 0 0 0.69s
✅ MARKDOWN markdown-table-formatter 2 0 0 0 0.27s
⚠️ PYTHON bandit 21 1 1 1.86s
✅ PYTHON ruff 21 0 0 0 0.03s
✅ PYTHON ruff-format 21 0 0 0 0.02s
✅ REPOSITORY checkov yes no no 14.0s
✅ REPOSITORY gitleaks yes no no 0.86s
✅ REPOSITORY git_diff yes no no 0.01s
✅ REPOSITORY grype yes no no 30.15s
✅ REPOSITORY secretlint yes no no 0.78s
✅ REPOSITORY semgrep yes no no 17.95s
✅ REPOSITORY syft yes no no 1.93s
✅ REPOSITORY trivy yes no no 7.69s
✅ REPOSITORY trivy-sbom yes no no 0.07s
✅ REPOSITORY trufflehog yes no no 2.49s
⚠️ SPELL lychee 2 5 0 0.38s

Detailed Issues

⚠️ PYTHON / bandit - 1 error
warning: Possible binding to all interfaces.
   ┌─ src/example_app/core/config.py:21:17
   │
21 │     host: str = "0.0.0.0"  # devskim: ignore DS162092
   │                 ^^^^^^^^^

warning: 1 warnings emitted
⚠️ SPELL / lychee - 5 errors
[ERROR] http://localhost:8000/redoc | Network error: error sending request for url (http://localhost:8000/redoc) Maybe a certificate error?
[ERROR] http://localhost:8000/ | Network error: error sending request for url (http://localhost:8000/) Maybe a certificate error?
[ERROR] http://localhost:8000/docs | Network error: error sending request for url (http://localhost:8000/docs) Maybe a certificate error?
[ERROR] http://localhost:8000/redoc | Error (cached)
[ERROR] http://localhost:8000/docs | Error (cached)
📝 Summary
---------------------
🔍 Total............6
✅ Successful.......1
⏳ Timeouts.........0
🔀 Redirected.......0
👻 Excluded.........0
❓ Unknown..........0
🚫 Errors...........5

Errors in QUICKSTART.md
[ERROR] http://localhost:8000/docs | Network error: error sending request for url (http://localhost:8000/docs) Maybe a certificate error?
[ERROR] http://localhost:8000/redoc | Network error: error sending request for url (http://localhost:8000/redoc) Maybe a certificate error?
[ERROR] http://localhost:8000/ | Network error: error sending request for url (http://localhost:8000/) Maybe a certificate error?

Errors in ARCHITECTURE.md
[ERROR] http://localhost:8000/docs | Error (cached)
[ERROR] http://localhost:8000/redoc | Error (cached)

See detailed reports in MegaLinter artifacts
Set VALIDATE_ALL_CODEBASE: true in mega-linter.yml to validate all sources, not only the diff

MegaLinter is graciously provided by OX Security

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This is an excellent and comprehensive refactoring of the todo application. You've successfully implemented a clean, layered architecture using the repository pattern and dependency injection, which greatly improves maintainability and testability. The transition from Gunicorn to fastapi run and the addition of a full test suite with dependency overrides are fantastic improvements. I have just a few minor suggestions to enhance the project further, mostly around documentation consistency and a performance optimization in the repository.

Comment on lines +151 to +156
statement = select(Task)

if completed is not None:
statement = statement.where(Task.completed == completed)

return len(list(self.session.exec(statement).all()))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current implementation of count is inefficient as it loads all matching task objects into memory just to get the length of the list. For a large number of tasks, this can cause significant performance issues and high memory usage. A much more efficient approach is to perform a COUNT query directly in the database.

You'll need to add from sqlmodel import func at the top of the file for this suggestion to work.

Suggested change
statement = select(Task)
if completed is not None:
statement = statement.where(Task.completed == completed)
return len(list(self.session.exec(statement).all()))
statement = select(func.count(Task.id))
if completed is not None:
statement = statement.where(Task.completed == completed)
return self.session.exec(statement).one()

@@ -0,0 +1,267 @@
# Task API Architecture

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This is a great architecture document! However, there are a few inconsistencies between the documentation and the actual code implementation that could confuse new developers:

  • Naming: The document frequently uses Todo (e.g., TodoCreate, TodoService), but the code consistently uses Task (e.g., TaskCreate, TaskService).
  • Endpoints: The API endpoints are documented with a /todos prefix (e.g., POST /todos), but the code implements them under /tasks.
  • Health Check: The main health check endpoint is documented as GET /, but the implementation is at GET /healthz.

Updating this document to align with the code would improve consistency.

DB_DIR.mkdir(exist_ok=True)

# Database URL - SQLite for simplicity
DATABASE_URL = f"sqlite:///{DB_DIR}/tasks.db"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The database filename tasks.db is hardcoded here. It would be more flexible to make the entire database URL configurable via the settings object. This would make it easier to use different database files or even different database systems for various environments (like development, testing, and production) without changing the code.

For example, you could define DATABASE_URL in your settings and use it here directly.

Reorganize package layout and update imports to match new structure.

- Move API-related code under src/example_app/api (including deps and v1 routes).
- Namespace task feature into src/example_app/features/tasks (models, schemas, repository, service).
- Move settings and logger into src/example_app/core (config.py, logger.py) and adjust logger to return loguru.logger and use InterceptHandler.
- Move database/session management into src/example_app/db/session.py and add db.base.Base model; create_db_and_tables docstring updated.
- Add package __init__ modules for api, api.v1, core, db, and features.
- Update main app factory to include api.v1 routers and call create_db_and_tables from new session module.
- Remove legacy routes module and update tests to use new import paths.

These changes refactor the project into clearer domains (api, core, db, features) and update references accordingly.
@yxtay
Copy link
Owner Author

yxtay commented Feb 27, 2026

/gemini review

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant and well-structured refactoring of the application, adopting a clean, layered architecture with a repository pattern and dependency injection. The changes greatly improve the project's modularity and maintainability. However, there are several critical issues in the Docker configuration that will prevent the application from being accessible and may cause performance degradation in production. Additionally, the documentation has several inconsistencies with the new implementation, and there are opportunities to improve performance and reduce code duplication in the data access and API layers.

ENV ENVIRONMENT=${ENVIRONMENT}
USER ${USER}
CMD ["gunicorn", "-c", "python:example_app.gunicorn_conf", "--reload"]
CMD ["fastapi", "run", "src/example_app/main.py", "--reload"]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The application inside the container will bind to 127.0.0.1 by default, which will make it inaccessible from the host machine. You should bind to 0.0.0.0 to expose the application outside the container.

CMD ["fastapi", "run", "src/example_app/main.py", "--host", "0.0.0.0", "--reload"]

ARG ENVIRONMENT=prod
ENV ENVIRONMENT=${ENVIRONMENT}
CMD ["gunicorn", "-c", "python:example_app.gunicorn_conf"]
CMD ["fastapi", "run", "src/example_app/main.py"]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The CMD for the prod stage uses fastapi run, which is primarily intended for development and runs a single worker process. For production, it's recommended to use a production-grade server like uvicorn directly with multiple worker processes to handle concurrent requests effectively. This change is a performance regression from the previous Gunicorn setup. Additionally, the application will bind to 127.0.0.1 by default, making it inaccessible from outside the container.

CMD ["uvicorn", "example_app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]

Comment on lines 37 to 54
def fastapi_app() -> FastAPI:
app = FastAPI()
"""Create and configure FastAPI application.

Returns:
Configured FastAPI application
"""
app = FastAPI(
title="Task API",
description="A simple Task API following repository pattern with dependency injection",
version="1.0.0",
lifespan=lifespan,
)

@app.get("/readyz")
@app.get("/livez")
@app.get("/")
async def ok() -> Ok:
return Ok()
# Include routers
app.include_router(health.router)
app.include_router(tasks.router)

return app

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The refactoring removed the / endpoint, but the HEALTHCHECK in the Dockerfile still points to it (HEALTHCHECK CMD ["curl", "-f", "http://localhost/"]). This will cause the health check to fail, and container orchestration systems might incorrectly flag the application as unhealthy. Please either restore a / endpoint for the health check or update the Dockerfile to use one of the new health endpoints (e.g., /healthz).

Comment on lines +151 to +156
statement = select(Task)

if completed is not None:
statement = statement.where(Task.completed == completed)

return len(list(self.session.exec(statement).all()))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The count method is inefficient as it loads all matching task objects into memory just to get their count using len(). This can lead to high memory consumption and slow performance with a large number of tasks. A more efficient approach is to perform a COUNT query directly in the database using sqlmodel.func.count(). You will need to import func from sqlmodel.

Suggested change
statement = select(Task)
if completed is not None:
statement = statement.where(Task.completed == completed)
return len(list(self.session.exec(statement).all()))
statement = select(func.count()).select_from(Task)
if completed is not None:
statement = statement.where(Task.completed == completed)
return self.session.exec(statement).one()

Comment on lines +27 to +101
- Pydantic models for request/response validation
- `TodoCreate`, `TodoUpdate`, `TodoResponse`
- Type-safe data transfer objects

#### 2. **Entities** (`entities.py`)

- SQLModel database models
- `Todo` entity with SQLAlchemy mappings

#### 3. **Repository** (`repository.py`)

- Abstract repository interface (`AbstractTodoRepository`)
- Concrete implementation (`TodoRepository`)
- Encapsulates all database operations
- Follows repository pattern for testability

#### 4. **Service** (`service.py`)

- Business logic layer (`TodoService`)
- Uses repository through dependency injection
- Converts between entities and schemas

#### 5. **Routes** (`routes.py`)

- FastAPI endpoint definitions
- Uses service through dependency injection
- HTTP status codes and error handling

#### 6. **Dependencies** (`dependencies.py`)

- FastAPI dependency injection setup
- Type-annotated dependencies
- Automatic dependency resolution

#### 7. **Database** (`database.py`)

- Database connection and session management
- Table creation on startup
- Session factory for dependency injection

## Dependency Injection Flow

```python
# 1. Database Session
SessionDep = Annotated[Session, Depends(get_session)]

# 2. Repository (depends on Session)
RepositoryDep = Annotated[TaskRepository, Depends(get_task_repository)]

# 3. Service (depends on Repository)
ServiceDep = Annotated[TaskService, Depends(get_task_service)]

# 4. Route (depends on Service)
@router.post("")
def create_task(task_data: TaskCreate, service: ServiceDep):
return service.create_task(task_data)
```

## API Endpoints

### Health Checks

- `GET /` - Health check
- `GET /readyz` - Readiness check
- `GET /livez` - Liveness check

### Todo Operations

- `POST /todos` - Create a new todo
- `GET /todos` - List all todos (with filtering & pagination)
- `GET /todos/{id}` - Get a specific todo
- `PUT /todos/{id}` - Update a todo
- `DELETE /todos/{id}` - Delete a todo
- `POST /todos/{id}/complete` - Mark todo as complete
- `POST /todos/{id}/incomplete` - Mark todo as incomplete

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This documentation contains several inconsistencies with the implementation:

  • Naming: It uses "Todo" (e.g., TodoCreate, TodoRepository) whereas the code uses "Task" (e.g., TaskCreate, TaskRepository).
  • Endpoints: It refers to endpoints under /todos, but they are implemented under /tasks. This also affects the curl examples.
    Please update the documentation to align with the code to avoid confusion.

Comment on lines +241 to +258
src/example_app/
├── __init__.py
├── main.py # Application entry point
├── routes.py # API endpoints
├── service.py # Business logic
├── repository.py # Data access layer
├── dependencies.py # DI configuration
├── database.py # Database setup
├── entities.py # SQLModel entities
├── schemas.py # Pydantic schemas
├── models.py # Utility models
├── settings.py # Configuration
└── logger.py # Logging setup

tests/
├── test_app.py # API tests
└── test_settings.py # Settings tests
```

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The project structure diagram is outdated and doesn't reflect the new, more modular structure introduced in this refactor. For example, files like routes.py, service.py, and repository.py are now located within sub-packages like api/v1/routes and features/tasks. Please update the diagram to accurately represent the current file layout.

Comment on lines +110 to +120
src/example_app/
├── main.py # Application entry + lifespan management
├── routes.py # API endpoints (HTTP layer)
├── service.py # Business logic
├── repository.py # Data access (Repository Pattern)
├── dependencies.py # Dependency injection configuration
├── database.py # Database session management
├── entities.py # SQLModel database entities
├── schemas.py # Pydantic request/response models
├── settings.py # Application configuration
└── logger.py # Logging setup

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The project structure diagram in this quick start guide is outdated. It doesn't reflect the new modular structure with sub-packages like api, core, db, and features. Please update the diagram to match the actual project layout to avoid confusion for new developers.

Comment on lines +88 to +94
task = service.get_task(task_id)
if not task:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Task with id {task_id} not found",
)
return task

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The pattern of fetching a task, checking if it's None, and raising a 404 HTTPException is repeated in get_task, update_task, delete_task, mark_complete, and mark_incomplete. This boilerplate can be reduced by creating a dedicated dependency that encapsulates this logic, making the route handlers cleaner and more focused on their primary action.

description: str | None = Field(None, max_length=1000)
completed: bool = Field(default=False, index=True)
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The updated_at field is only set on creation. While you are manually updating it in the repository's update method, it's more robust to have this handled automatically by SQLAlchemy whenever the model is updated. You can achieve this using onupdate in sa_column_kwargs. This ensures consistency and reduces the chance of forgetting to update the timestamp. You will need to import func from sqlalchemy.

Suggested change
updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC), sa_column_kwargs={"onupdate": func.now()})

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
# gunicorn
host: str = "127.0.0.1" # devskim: ignore DS162092
# Server configuration
host: str = "0.0.0.0" # devskim: ignore DS162092

Check warning

Code scanning / Bandit (MegaLinter PYTHON_BANDIT)

Possible binding to all interfaces. Warning

Possible binding to all interfaces.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant