Module 6: Project — CRUD API (To-Do List)
Entrega y Evaluación
Descripción
Cierras el Módulo 6 con el código completo del To-Do List API, una rúbrica de evaluación de 100 puntos, errores comunes a evitar, una retrospectiva 4Ls y la conexión con lo que viene después: FastAPI Advanced Features Guide.
Código completo de referencia
El API completo está en la Cápsula 05. A continuación tienes la especificación detallada de lo que debe incluir tu implementación final.
Modelos Pydantic (4 modelos, 18 campos en total)
| Modelo | Campos | Descripción |
|---|---|---|
| TaskCreate | 4 campos: title, description, status, priority | Request para POST. title con min_length=1, max_length=200. status y priority con Literal. |
| TaskUpdate | 4 campos opcionales: title, description, status, priority | Request para PATCH. Todos Optional. |
| TaskResponse | 6 campos: id, title, description, status, priority, created_at | Response en GET, POST, PUT, PATCH. Incluye from_attributes=True. |
| TaskListResponse | 4 campos: total, skip, limit, items | Response para GET /tasks paginado. |
Endpoints disponibles (7 rutas, 8 operaciones)
| Método | Ruta | Status codes | Descripción |
|---|---|---|---|
| GET | / | 200 | Info del servicio: service, version, total_tasks |
| GET | /tasks | 200 | Lista con status, priority, search, sort_by, order, skip, limit |
| GET | /tasks/{task_id} | 200, 404 | Obtener tarea por ID |
| POST | /tasks | 201, 422 | Crear tarea |
| PUT | /tasks/{task_id} | 200, 404, 422 | Actualizar completa |
| PATCH | /tasks/{task_id} | 200, 404, 422 | Actualizar parcial |
| DELETE | /tasks/{task_id} | 200, 404 | Eliminar tarea |
Exception handlers
| Handler | Excepción | Formato de salida |
|---|---|---|
| http_exception_handler | HTTPException | `{"success": false, "error": {"code": "NOT_FOUND" |
| validation_exception_handler | RequestValidationError | Igual que arriba con code "VALIDATION_ERROR", más campo opcional "details" con los errores de Pydantic |
CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Estructura exacta del proyecto
todo-api/
├── venv/
├── app/
│ ├── __init__.py
│ └── main.py ← ~250-350 líneas
├── requirements.txt ← fastapi>=0.115.0, uvicorn[standard]>=0.32.0
└── .gitignore ← venv/, __pycache__/, *.pyc, .env
Funciones auxiliares obligatorias
next_id()— Devuelve max(id) + 1find_task(task_id: int)— Devuelve el dict o None_status_to_code(status: int)— Mapea 404→NOT_FOUND, 400→BAD_REQUEST, etc.PRIORITY_ORDER— Dict para ordenar prioridad: high=0, medium=1, low=2
Ejemplo de uso con curl
# Listar tareas
curl http://localhost:8000/tasks
# Filtrar por status
curl "http://localhost:8000/tasks?status=pending"
# Crear tarea
curl -X POST http://localhost:8000/tasks \
-H "Content-Type: application/json" \
-d '{"title": "Nueva tarea", "priority": "high"}'
# Actualizar parcial
curl -X PATCH http://localhost:8000/tasks/1 \
-H "Content-Type: application/json" \
-d '{"status": "completed"}'
El código fuente completo está en la Cápsula 05. Copia app/main.py de ahí como referencia final.
Autoevaluación guiada
Sigue estos pasos con el servidor corriendo (uvicorn app.main:app --reload). Marca los puntos y suma al final.
Paso 1: Levantar y verificar servicio (base)
- Ejecuta
uvicorn app.main:app --reloaddesde la carpetatodo-apicon el venv activado. - ¿Arranca sin errores? Si sí, continúa.
- Abre
http://localhost:8000/y verifica que recibes JSON conservice,versionytotal_tasks. - Resultado esperado:
{"service": "To-Do List API", "version": "1.0.0", "total_tasks": N}con N ≥ 0. Puntos: base para evaluar Funcionalidad.
Paso 2: Funcionalidad — GET / y GET /tasks (12 pts)
| Acción | Cómo probar | Resultado esperado | pts |
|---|---|---|---|
| GET / | curl http://localhost:8000/ | JSON con service, version, total_tasks | 6 |
| GET /tasks | curl http://localhost:8000/tasks | {"total": N, "skip": 0, "limit": 10, "items": [...]} | 3 |
| Filtros | curl "http://localhost:8000/tasks?status=pending&priority=high&search=API" | Lista filtrada correctamente | 3 |
Si los tres funcionan: 12 pts.
Paso 3: Funcionalidad — CRUD (18 pts)
| Acción | Cómo probar | Resultado esperado | pts |
|---|---|---|---|
| POST | POST /tasks con {"title": "Test"}, verificar 201 y que tenga id y created_at | 201, body con id y created_at | 6 |
| GET/PUT/PATCH/DELETE | Crear tarea 4, luego GET, PUT, PATCH, DELETE | Todas las operaciones responden correctamente | 6 |
| PATCH exclude_unset | PATCH con solo {"status": "completed"} | Solo status cambia, title/description intactos | 6 |
Si todo funciona: 18 pts. Funcionalidad total: 30 pts.
Paso 4: Validación (25 pts)
| Acción | Cómo probar | Resultado esperado | pts |
|---|---|---|---|
| TaskCreate constraints | POST con {"title": ""} o {"status": "invalid"} | 422 con formato success/error/status_code | 8 |
| TaskUpdate Optional | PATCH con {} o solo {"status": "completed"} | No errores, actualización parcial correcta | 6 |
| Query params | GET con ?skip=-1 o ?limit=0 | 422 o rechazo de valores inválidos | 5 |
| Datos inválidos 422 | Cualquier body mal formado | Respuesta con code VALIDATION_ERROR | 6 |
Si cumples lo anterior: 25 pts.
Paso 5: Error handling (20 pts)
| Acción | Cómo probar | Resultado esperado | pts |
|---|---|---|---|
| 404 en GET/PUT/PATCH/DELETE | GET /tasks/999, PUT/PATCH/DELETE con id inexistente | JSON con success: false, error.code: "NOT_FOUND" | 8 |
| Handler HTTPException | Cualquier 404, 400 | Formato consistente en todas las respuestas | 6 |
| Handler RequestValidationError | POST con title vacío | Formato con code "VALIDATION_ERROR" | 6 |
Si todo tiene formato consistente: 20 pts.
Paso 6: Organización y documentación (25 pts)
| Criterio | Cómo evaluar | pts |
|---|---|---|
| Código limpio | Revisar main.py: sin duplicación, funciones claras | 5 |
| Modelos bien definidos | TaskCreate, TaskUpdate, TaskResponse, TaskListResponse separados | 5 |
| Funciones auxiliares | next_id, find_task usadas en los endpoints | 5 |
| FastAPI metadata | /docs muestra title, description, version | 5 |
| CORS y tags | CORS configurado, tags en endpoints | 5 |
Total máximo: 100 pts. Si obtienes 90+, estás en nivel Excelente.
Consejos para la autoevaluación
- Usa la pestaña "Try it out" de
/docspara probar cada endpoint sin instalar curl. - Si un criterio falla, anota el número del paso y el resultado real vs esperado; te ayudará a depurar.
- No te saltes el Paso 5: el formato de error consistente es uno de los diferenciadores más visibles para quien consuma tu API.
Criterios de evaluación detallados
Al evaluar tu proyecto, revisa que cada criterio cumpla lo esperado. La rúbrica suma 100 puntos.
Rúbrica de evaluación (100 puntos)
Funcionalidad (30 pts)
- (6) GET / retorna info del servicio
- (6) GET /tasks con filtros (status, priority, search), ordenamiento y paginación
- (6) GET /tasks/{id}, POST, PUT, PATCH, DELETE funcionan correctamente
- (6) POST retorna 201 y asigna id y created_at
- (6) PATCH actualiza solo campos enviados (exclude_unset)
Validación (25 pts)
- (8) TaskCreate con Field constraints (title min 1, Literal para status/priority)
- (6) TaskUpdate con campos Optional
- (6) Datos inválidos retornan 422
- (5) Query params (skip, limit, sort_by, order) validados
Error handling (20 pts)
- (8) HTTPException para 404 en todos los endpoints que lo requieren
- (6) Exception handler para HTTPException con formato consistente
- (6) Exception handler para RequestValidationError
Organización (15 pts)
- (5) Código limpio, sin duplicación innecesaria
- (5) Modelos Pydantic bien definidos y separados
- (5) Funciones auxiliares (next_id, find_task) usadas correctamente
Documentación (10 pts)
- (5) FastAPI con title, description, version
- (5) CORS configurado, tags en endpoints
Guía de puntuación
| Rango | Nivel | Descripción |
|---|---|---|
| 90-100 | Excelente | API completa, código limpio, sin errores de ejecución |
| 80-89 | Muy bien | Cumple todo, pequeños ajustes posibles |
| 70-79 | Bien | Funcionalidad completa, mejorar organización o validación |
| 60-69 | Aceptable | Funciona pero falta pulido (handlers, CORS, docs) |
| < 60 | En progreso | Revisar cápsulas anteriores y completar endpoints faltantes |
Checklist de entrega
- Proyecto ejecuta sin errores:
uvicorn app.main:app --reload - GET / retorna servicio, versión y total_tasks
- GET /tasks con status, priority, search, sort_by, order, skip, limit
- GET /tasks/{id} retorna 404 si no existe
- POST /tasks crea tarea y retorna 201
- PUT /tasks/{id} actualiza completa preservando created_at
- PATCH /tasks/{id} actualiza parcial con exclude_unset
- DELETE /tasks/{id} elimina y retorna confirmación
- Errores con formato consistente (success, error, status_code)
- CORS habilitado
- /docs muestra documentación correcta
Flujo de verificación completo
Ejecuta en orden para validar el CRUD de extremo a extremo. Respuestas esperadas incluidas.
| Paso | Acción | Comando o body | Respuesta esperada |
|---|---|---|---|
| 1 | GET / | curl http://localhost:8000/ | {"service":"To-Do List API","version":"1.0.0","total_tasks":3} |
| 2 | GET /tasks | curl http://localhost:8000/tasks | {"total":3,"skip":0,"limit":10,"items":[{...},{...},{...}]} |
| 3 | POST /tasks | Body: {"title":"Tarea 4","priority":"high"} | 201, {"id":4,"title":"Tarea 4","description":"","status":"pending","priority":"high","created_at":"..."} |
| 4 | GET /tasks/4 | curl http://localhost:8000/tasks/4 | 200, tarea recién creada con id 4 |
| 5 | POST inválido | Body: {"title":""} | 422, {"success":false,"error":{"code":"VALIDATION_ERROR",...},"status_code":422} |
| 6 | PUT /tasks/4 | Body completo: title, description, status, priority | 200, tarea actualizada, created_at sin cambiar |
| 7 | PATCH /tasks/4 | Body: {"status":"completed"} | 200, tarea con status "completed", resto igual |
| 8 | GET /tasks/4 | curl http://localhost:8000/tasks/4 | 200, "status":"completed" |
| 9 | GET /tasks?status=completed | curl "http://localhost:8000/tasks?status=completed" | Lista filtrada con al menos la tarea 4 |
| 10 | DELETE /tasks/4 | curl -X DELETE http://localhost:8000/tasks/4 | 200, {"message":"Task deleted","id":4} |
| 11 | GET /tasks/4 | curl http://localhost:8000/tasks/4 | 404, {"success":false,"error":{"code":"NOT_FOUND",...},"status_code":404} |
Si los 11 pasos coinciden con las respuestas esperadas, tu API pasa la verificación completa.
Comparación con APIs profesionales
Lo que esta API tiene ✅
- CRUD completo con métodos HTTP correctos
- Validación Pydantic (campos, Literal, constraints)
- Error handling con formato consistente
- CORS para consumo desde frontend
- Documentación automática (/docs, /redoc)
- Paginación, filtros y ordenamiento en GET /tasks
Esto demuestra que dominas los fundamentos de una API REST moderna.
Lo que falta (y dónde aprenderlo)
| Aspecto | Estado actual | Guía o módulo futuro |
|---|---|---|
| Autenticación | No hay login ni tokens | Authentication & Authorization Guide |
| Base de datos persistente | Solo en memoria | PostgreSQL & SQLAlchemy Guide |
| Tests automatizados | No hay pytest | Testing Guide |
| Despliegue | Local únicamente | Docker, CI/CD guides |
| Rate limiting | No hay protección | FastAPI Advanced Features |
| Logging estructurado | Solo prints/default | Production Best Practices |
No te preocupes por lo que falta: este proyecto es el punto de partida. Las guías siguientes te llevarán paso a paso a añadir cada capa.
Resumen visual
Tu To-Do API actual → API de producción
─────────────────────────────────────────────────
CRUD ✅ CRUD ✅
Validación ✅ Validación ✅
Error handling ✅ Error handling ✅
CORS ✅ CORS ✅
Docs ✅ Docs ✅
En memoria PostgreSQL/SQLAlchemy
Sin auth JWT/OAuth2
Sin tests pytest + cobertura
Local Docker + CI/CD
Errores comunes
PATCH sobrescribe con None
Si usas model_dump() sin exclude_unset=True, los campos no enviados se serializan como None y sobrescriben valores existentes. Siempre usa model_dump(exclude_unset=True) para PATCH.
PUT modifica created_at
En PUT debes preservar created_at del recurso original. No lo reemplaces con datetime.utcnow().
Filtros con valores inválidos
Si status o priority aceptan cualquier string, el filtro puede devolver lista vacía sin avisar. Considera validar con Literal en los query params o documentar los valores válidos claramente.
Ordenamiento por priority sin mapeo
Si ordenas priority como string, "high" < "low" alfabéticamente. Usa un diccionario de orden (PRIORITY_ORDER) para prioridad semántica correcta.
CORS en producción
allow_origins=["*"] es cómodo en desarrollo pero inseguro en producción. Define orígenes concretos cuando despliegues.
Troubleshooting de entrega
requirements.txt faltante o incompleto
Problema: Quien clone tu repo no puede instalar dependencias.
Solución: Crea requirements.txt en la raíz con:
fastapi>=0.115.0
uvicorn[standard]>=0.32.0
Verifica con pip install -r requirements.txt en un venv nuevo antes de subir.
venv incluido en git
Problema: La carpeta venv/ se sube al repositorio y ocupa mucho espacio.
Solución: Añade venv/ a .gitignore. Si ya lo subiste, elimínalo del tracking: git rm -r --cached venv/ y haz commit.
CORS bloqueando tests desde frontend
Problema: Un frontend en localhost:3000 no puede llamar a localhost:8000; el navegador muestra error CORS.
Solución: Confirma que CORSMiddleware está registrado con allow_origins=["*"] (o incluye tu origen). Revisa en DevTools (Network) que la respuesta OPTIONS devuelva 200 y los headers Access-Control-Allow-Origin y Access-Control-Allow-Methods.
Puerto 8000 en uso
Problema: "Address already in use" al ejecutar uvicorn.
Solución: Usa otro puerto: uvicorn app.main:app --reload --port 8001. Ajusta las URLs de prueba.
ImportError al correr uvicorn
Problema: "No module named 'app'" o "No module named 'fastapi'".
Solución: Ejecuta uvicorn desde la carpeta que contiene app/ (la raíz del proyecto). Activa el venv y comprueba que pip list muestra fastapi y uvicorn.
Qué viene después: FastAPI Advanced Features Guide
Has completado los fundamentos. La guía avanzada agrega:
| Tema | Qué aprenderás | Aplicación al To-Do API |
|---|---|---|
| Dependency Injection | Servicios, conexiones a BD, auth como dependencias inyectables | Separar lógica de negocio en servicios reutilizables |
| APIRouter | Estructura modular, rutas por dominio | Mover endpoints de tareas a routers/tasks.py |
| WebSockets | Comunicación en tiempo real | Notificaciones cuando se crea/actualiza una tarea |
| Background Tasks | Procesamiento asíncrono sin bloquear la respuesta | Envío de emails, generación de reportes |
| File uploads y streaming | Archivos grandes, descargas | Adjuntar archivos a tareas |
| Middleware avanzado | Logging, metrics, rate limiting | Proteger la API de abuso |
El To-Do List API que construiste es la base conceptual. En la guía avanzada lo evolucionarás con bases de datos reales (PostgreSQL + SQLAlchemy), autenticación, WebSockets para notificaciones en vivo y arquitectura modular.
Otros guías en el path
- PostgreSQL & SQLAlchemy — Persistencia real: reemplazar
tasks_dbpor tablas y sesiones. Las tareas sobrevivirán reinicios. - Authentication & Authorization — Proteger endpoints con JWT, roles y permisos. Solo usuarios autenticados podrían crear o editar tareas.
Portfolio tips
Plantilla README.md
Copia esto como base para tu README. En la sección Setup, usa un bloque de código bash:
# To-Do List API
API REST con FastAPI para gestionar tareas. Proyecto del Módulo 6 — FastAPI Fundamentals.
## Tecnologías
- FastAPI
- Pydantic
- Uvicorn
## Setup
git clone <tu-repo>
cd todo-api
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
uvicorn app.main:app --reload
## Endpoints
- GET / — Info del servicio
- GET /tasks — Lista con filtros, paginación, ordenamiento
- GET /tasks/{id} — Obtener tarea
- POST /tasks — Crear tarea
- PUT /tasks/{id} — Actualizar completa
- PATCH /tasks/{id} — Actualizar parcial
- DELETE /tasks/{id} — Eliminar tarea
## Documentación
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
Qué destacar en entrevistas
- Arquitectura REST: Uso correcto de métodos HTTP (GET, POST, PUT, PATCH, DELETE) y códigos de estado (200, 201, 404, 422).
- Validación con Pydantic: Separación de modelos Create/Update/Response, Literal para enums, Field constraints.
- Error handling: Exception handlers con formato JSON consistente para HTTPException y RequestValidationError.
- CORS: Capacidad de consumir la API desde un frontend en otro origen.
Cómo extender para impresionar
- Añadir
GET /tasks/statsque devuelva conteo por status — demuestra agregaciones. - Implementar persistencia en JSON (guardar/cargar
tasks_dben archivo) — demuestra I/O. - Crear un frontend mínimo (HTML + fetch) que consuma la API — demuestra integración full-stack.
Repositorio GitHub
Si subes el proyecto a GitHub, incluye un archivo .gitignore con al menos venv/, __pycache__/ y *.pyc. Etiqueta el repo con topics como fastapi, python, rest-api para que sea más fácil de encontrar. Un buen README con instrucciones claras y ejemplos de uso aumenta mucho el valor del proyecto en tu portfolio.
Retrospectiva 4Ls
Reflexiona y, si puedes, escribe tus respuestas. Aproximadamente 2-3 frases por apartado.
Loved (me encantó)
Pregunta: ¿Qué disfrutaste más al construir este proyecto?
Ejemplos para inspirarte:
- Integrar todo lo aprendido en un proyecto completo
- Ver cómo Pydantic, path operations, query params y error handling encajan
- Tener una API funcional lista para portfolio
Learned (aprendí)
Pregunta: ¿Qué conceptos o técnicas consolidaste?
Ejemplos:
- Cómo construir un CRUD completo con FastAPI
- Separación de modelos Create, Update, Response
- Formato consistente de errores con exception handlers
- Filtros, ordenamiento y paginación en un solo endpoint
Lacked (faltó)
Pregunta: ¿Qué echaste en falta o qué se quedó a medias?
Ejemplos:
- Base de datos persistente (por ahora en memoria)
- Autenticación y autorización
- Tests automatizados
Longed for (me hubiera gustado)
Pregunta: ¿Qué te habría gustado hacer si tuvieras más tiempo?
Ejemplos:
- Más tiempo para extender (categorías, due dates, etc.)
- Integrar con un frontend real
Ideas para extender (opcional)
- Agregar campo
due_dateopcional con validación de fecha futura - Implementar categorías o etiquetas para tareas
- Agregar endpoint
GET /tasks/statsque retorne conteo por status - Persistencia con JSON (guardar/cargar tasks_db en archivo)
- README.md con instrucciones de setup y ejemplos con curl
Consejos finales antes de entregar
Antes de dar por cerrada la entrega, revisa estos puntos:
- Reinicia el servidor — Cierra uvicorn, vuelve a ejecutarlo y comprueba que todo arranca sin errores. A veces un cambio reciente introduce un bug que solo se revela al reiniciar.
- Prueba el flujo completo — Sigue la tabla del "Flujo de verificación completo" de principio a fin en una sola sesión. Si falla algún paso, corrige antes de entregar.
- Revisa la rúbrica — Pasa por cada ítem de la rúbrica y comprueba que cumples el criterio. Si dudas en alguno, vuelve a la cápsula correspondiente.
- Limpia el código — Elimina comentarios de depuración, prints y código comentado que no aporte. Un main.py limpio refleja profesionalidad.
- Prepara el README — Si vas a compartir el proyecto (GitHub, portfolio), usa la plantilla de la sección "Portfolio tips" para que cualquiera pueda ejecutarlo sin preguntar.
Consejos finales antes de entregar
Antes de dar por cerrada la entrega, haz esta comprobación rápida:
- Reinicia el servidor desde cero — Cierra uvicorn, activa el venv en una terminal nueva y ejecuta de nuevo. Así verificas que no hay dependencias de sesiones previas.
- Prueba el flujo completo en una sesión limpia — Reinicia el servidor (las tareas volverán a las 3 iniciales) y ejecuta los 11 pasos del flujo de verificación. Todo debe funcionar de principio a fin.
- Revisa /docs — Abre Swagger UI y confirma que cada endpoint muestra los parámetros correctos y que "Try it out" responde como esperas.
- Cierra la retrospectiva 4Ls — Escribir aunque sea unas líneas en cada L te ayuda a fijar lo aprendido y a identificar qué quieres profundizar después.
Conexión con el path de aprendizaje
Este proyecto completa la Guía #6: FastAPI Fundamentals del path Backend Python Developer. Siguiente guía en el path:
- Guía #7: FastAPI Advanced Features — Dependency injection, APIRouter, WebSockets, background tasks, middleware avanzado
El To-Do List API que construiste sirve como base conceptual. En la guía avanzada lo extenderás con persistencia real, autenticación y arquitectura modular.
Resumen
Has construido una To-Do List API completa que integra:
- ✅ Setup y estructura de proyecto
- ✅ Path operations (GET, POST, PUT, PATCH, DELETE)
- ✅ Request/Response con path y query params
- ✅ Pydantic para validación y modelos
- ✅ Error handling profesional con formato consistente
- ✅ CORS para consumo desde frontend
Este proyecto demuestra dominio de los fundamentos de FastAPI y está listo para tu portfolio. La guía FastAPI Fundamentals queda completada.
Recursos adicionales
- FastAPI Documentation — Documentación oficial
- Pydantic Documentation — Validación y modelos
- FastAPI Advanced User Guide — Dependency injection, APIRouter, middleware
- REST API Best Practices — Convenciones REST
- FastAPI — Handling Errors — Exception handlers y códigos de error
- MDN — CORS — Entender la política same-origin y los headers CORS
Módulo 6, Cápsula 06 — FastAPI Fundamentals Guide