diff --git a/README.md b/README.md index e8acfba..2ece34f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,68 @@ -# FastAPI Application +# Simple Todo App -This is a FastAPI application bootstrapped by BackendIM, the AI-powered backend generation platform. +This is a simple Todo application built with FastAPI and SQLite. It provides basic CRUD operations for managing todo items through a RESTful API. + +## Features + +- Create, Read, Update, and Delete todo items +- Health check endpoint +- SQLite database storage +- RESTful API with JSON payloads +- Automatic API documentation + +## Installation + +1. Clone the repository +2. Install dependencies: +```bash +pip install -r requirements.txt +``` + +## Running the Application + +Start the application with uvicorn: + +```bash +uvicorn main:app --reload +``` + +The application will be available at http://localhost:8000 + +## API Documentation + +Once the application is running, you can access the auto-generated documentation: + +- Swagger UI: http://localhost:8000/docs +- ReDoc: http://localhost:8000/redoc + +## API Endpoints + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | /health | Health check endpoint | +| GET | /todos/ | Get all todos | +| GET | /todos/{todo_id} | Get a specific todo by ID | +| POST | /todos/ | Create a new todo | +| PUT | /todos/{todo_id} | Update an existing todo | +| DELETE | /todos/{todo_id} | Delete a todo | + +## Example Usage + +### Creating a Todo + +```bash +curl -X 'POST' \ + 'http://localhost:8000/todos/' \ + -H 'Content-Type: application/json' \ + -d '{ + "title": "Buy groceries", + "description": "Need to buy milk, eggs, and bread", + "completed": false +}' +``` + +### Getting All Todos + +```bash +curl -X 'GET' 'http://localhost:8000/todos/' +``` \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..0ab55fc --- /dev/null +++ b/main.py @@ -0,0 +1,143 @@ +from fastapi import FastAPI, HTTPException, status +from pydantic import BaseModel +from typing import List, Optional +from pathlib import Path +import uuid +import sqlite3 + +# Create the FastAPI app +app = FastAPI( + title="Simple Todo App", + description="A very simple todo application using FastAPI and SQLite", + version="0.1.0", +) + +# Ensure database directory exists +DB_DIR = Path("/app") / "storage" / "db" +DB_DIR.mkdir(parents=True, exist_ok=True) + +# Database setup +SQLALCHEMY_DATABASE_URL = f"sqlite:///{DB_DIR}/db.sqlite" +DB_PATH = f"{DB_DIR}/db.sqlite" + +def init_db(): + """Initialize the SQLite database with the todos table if it doesn't exist.""" + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + cursor.execute(''' + CREATE TABLE IF NOT EXISTS todos ( + id TEXT PRIMARY KEY, + title TEXT NOT NULL, + description TEXT, + completed BOOLEAN NOT NULL DEFAULT 0 + ) + ''') + conn.commit() + conn.close() + +# Initialize database on startup +init_db() + +# Pydantic models +class TodoBase(BaseModel): + title: str + description: Optional[str] = None + completed: bool = False + +class TodoCreate(TodoBase): + pass + +class Todo(TodoBase): + id: str + + class Config: + from_attributes = True + +# Health check endpoint +@app.get("/health", status_code=status.HTTP_200_OK, tags=["health"]) +async def health_check(): + """Return health status of the application.""" + return {"status": "healthy"} + +# CRUD Operations +@app.post("/todos/", response_model=Todo, status_code=status.HTTP_201_CREATED, tags=["todos"]) +async def create_todo(todo: TodoCreate): + """Create a new todo item.""" + todo_id = str(uuid.uuid4()) + + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + cursor.execute( + "INSERT INTO todos (id, title, description, completed) VALUES (?, ?, ?, ?)", + (todo_id, todo.title, todo.description, todo.completed) + ) + conn.commit() + conn.close() + + return {**todo.model_dump(), "id": todo_id} + +@app.get("/todos/", response_model=List[Todo], tags=["todos"]) +async def read_todos(): + """Get all todo items.""" + conn = sqlite3.connect(DB_PATH) + conn.row_factory = sqlite3.Row + cursor = conn.cursor() + cursor.execute("SELECT * FROM todos") + rows = cursor.fetchall() + conn.close() + + return [dict(row) for row in rows] + +@app.get("/todos/{todo_id}", response_model=Todo, tags=["todos"]) +async def read_todo(todo_id: str): + """Get a specific todo item by ID.""" + conn = sqlite3.connect(DB_PATH) + conn.row_factory = sqlite3.Row + cursor = conn.cursor() + cursor.execute("SELECT * FROM todos WHERE id = ?", (todo_id,)) + row = cursor.fetchone() + conn.close() + + if row is None: + raise HTTPException(status_code=404, detail="Todo not found") + + return dict(row) + +@app.put("/todos/{todo_id}", response_model=Todo, tags=["todos"]) +async def update_todo(todo_id: str, todo: TodoCreate): + """Update a todo item.""" + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + cursor.execute( + "UPDATE todos SET title = ?, description = ?, completed = ? WHERE id = ?", + (todo.title, todo.description, todo.completed, todo_id) + ) + + if cursor.rowcount == 0: + conn.close() + raise HTTPException(status_code=404, detail="Todo not found") + + conn.commit() + conn.close() + + return {**todo.model_dump(), "id": todo_id} + +@app.delete("/todos/{todo_id}", status_code=status.HTTP_204_NO_CONTENT, response_model=None, tags=["todos"]) +async def delete_todo(todo_id: str): + """Delete a todo item.""" + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + cursor.execute("DELETE FROM todos WHERE id = ?", (todo_id,)) + + if cursor.rowcount == 0: + conn.close() + raise HTTPException(status_code=404, detail="Todo not found") + + conn.commit() + conn.close() + + return None + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..50e55b6 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +fastapi>=0.101.0 +uvicorn>=0.23.2 +pydantic>=2.0.0 +python-multipart>=0.0.6 +ruff>=0.0.270 +sqlalchemy>=2.0.0 \ No newline at end of file