Objetivo del tema
Consolidar el flujo de trabajo con OpenCode en otro ecosistema: construir una pequeña API de tareas con Python y FastAPI, con validación de datos mediante Pydantic y pruebas automatizadas usando Pytest.
A diferencia del servicio estático del tema anterior, esta API tendrá estado interno y dos operaciones: listar tareas y crear tareas con validación. El recorrido:
Crear entorno virtual, instalar FastAPI, Uvicorn, Pytest y Httpx; inicializar Git.
/init para generar el AGENTS.md con comandos Python del proyecto.
Diseño de la estructura, modelos Pydantic y casos de prueba.
Implementación de rutas, modelos y suite de pruebas.
Ejecutar Pytest, revisar el diff y crear el commit.
mkdir api-tareas && cd api-tareas
git init
python -m venv .venv
source .venv/bin/activate # Windows PowerShell: .venv\Scripts\Activate.ps1
pip install fastapi uvicorn
pip install --dev pytest httpx
Crea un requirements.txt para dejar fijadas las dependencias (pip freeze > requirements.txt) y un pytest.ini mínimo:
[pytest]
testpaths = tests
Abre el agente y ejecuta /init. Al detectar el entorno Python, el AGENTS.md resultante documentará cómo activar el entorno virtual y correr los tests. Agrega tus convenciones si hace falta:
python -m pytest antes de considerar terminada una tarea".Cambia a Plan con Tab y plantea el diseño:
Diseña una API de tareas en memoria (sin base de datos): GET /tareas devuelve la lista completa; POST /tareas crea una tarea validada con Pydantic (id autogenerado, titulo obligatorio de 1 a 80 caracteres, completada booleana con valor por defecto False). Organiza el código en main.py y models.py. Escribe tests/test_tareas.py con TestClient cubriendo: lista vacía inicial, creación exitosa, error 422 con titulo vacío o demasiado largo, y que el id incrementa entre creaciones. Ejecuta python -m pytest al final.
El plan devuelto debería incluir la separación de responsabilidades (rutas vs modelos), el uso de un contador o lista para los ids y el orden de implementación. Itera hasta que te convenza: por ejemplo "agrega también DELETE /tareas/{id} con respuesta 404 si no existe".
Vuelve a Build, aprueba y deja que implemente. El resultado esperado:
# models.py
from pydantic import BaseModel, Field
class Tarea(BaseModel):
id: int
titulo: str = Field(min_length=1, max_length=80)
completada: bool = False
class TareaEntrada(BaseModel):
titulo: str = Field(min_length=1, max_length=80)
completada: bool = False
# main.py
from fastapi import FastAPI, HTTPException
from models import Tarea, TareaEntrada
app = FastAPI(title="API de tareas")
_tareas: list[Tarea] = []
_siguiente_id = 1
@app.get("/tareas", response_model=list[Tarea])
def listar_tareas():
return _tareas
@app.post("/tareas", response_model=Tarea, status_code=201)
def crear_tarea(entrada: TareaEntrada):
global _siguiente_id
tarea = Tarea(id=_siguiente_id, **entrada.model_dump())
_siguiente_id += 1
_tareas.append(tarea)
return tarea
# tests/test_tareas.py
from fastapi.testclient import TestClient
from main import app
cliente = TestClient(app)
def test_lista_vacia_inicial():
respuesta = cliente.get("/tareas")
assert respuesta.status_code == 200
assert respuesta.json() == []
def test_crear_tarea_exitosa():
respuesta = cliente.post("/tareas", json={"titulo": "Estudiar OpenCode"})
assert respuesta.status_code == 201
cuerpo = respuesta.json()
assert cuerpo["id"] == 1
assert cuerpo["completada"] is False
def test_titulo_invalido_devuelve_422():
respuesta = cliente.post("/tareas", json={"titulo": ""})
assert respuesta.status_code == 422
| Verificación | Cómo | Esperado |
|---|---|---|
| Suite de pruebas | python -m pytest -v |
Todos los tests en verde. |
| Prueba manual | uvicorn main:app --reload y abrir /docs |
La documentación interactiva de FastAPI permite probar POST y GET. |
| Validación negativa | POST con título vacío desde /docs o curl | Respuesta 422 con detalle del campo inválido. |
| Revisión del diff | git diff |
Sólo main.py, models.py y los tests. |
git add -A
git commit -m "feat: API de tareas con validacion Pydantic y tests Pytest"
Ejercicios para seguir practicando con el mismo proyecto:
PATCH /tareas/{id} para marcar una tarea como completada (con 404 si no existe).opencode run para correr Pytest en cada push.¿Notaste la diferencia con el tema 10? Aquí el prompt incluyó reglas de validación explícitas (límites del título, códigos 422/404). Cuando tu dominio tiene reglas, escribirlas en el prompt inicial es más barato que corregirlas después.
Conclusión: con este segundo proyecto el flujo ya es automático: preparar, inicializar, planificar, construir y validar. Lo has aplicado sobre dos ecosistemas distintos, lo que confirma que el método funciona igual sin importar el lenguaje. En el próximo tema pasaremos a las integraciones con editores e IDEs.