Validación de Datos y Modelos en Endpoints de FastAPI

Clase 7 de 23Curso de FastAPI

Contenido del curso

Resumen

Controlar qué datos recibe y qué datos devuelve cada endpoint es una de las decisiones más importantes al construir una API. Con Pydantic y FastAPI puedes separar claramente el modelo de creación del modelo de respuesta, evitando exponer información sensible como el ID interno y garantizando que cada campo se valide antes de llegar a tu lógica de negocio.

¿Por qué separar los modelos de creación y respuesta?

Cuando defines un único modelo que incluye el ID, el formulario de tu documentación interactiva lo solicita al usuario, aunque ese valor debería generarse automáticamente en el backend [0:30]. La solución es aplicar herencia de clases en Python para crear modelos especializados.

  • CustomerCreate: modelo base sin ID, destinado a recibir datos del usuario.
  • Customer: hereda de la clase base y agrega el campo id como int | None con valor por defecto None [1:25].

Esta técnica aprovecha la herencia de Pydantic: la clase hija conserva todos los campos de la clase padre y solo añade o modifica lo necesario. Así evitas duplicar líneas y mantienes un único punto de verdad para los campos compartidos como name, email o age [0:55].

¿Cómo se define el response model en el decorador?

FastAPI permite indicar con qué modelo responderá cada ruta mediante el parámetro response_model dentro del decorador @app.post [2:28]. De esta forma:

  • El request body usa CustomerCreate (sin ID).
  • La respuesta usa Customer (con ID).

python @app.post("/customers", response_model=Customer) async def create_customer(customer_data: CustomerCreate): customer = Customer.model_validate(customer_data.model_dump()) customer.id = len(db_customers) db_customers.append(customer) return customer

El método model_validate recibe un diccionario y ejecuta todas las validaciones definidas en el modelo. Si algún campo no cumple las reglas, FastAPI devuelve un error automáticamente sin que escribas lógica adicional [3:05]. Por otro lado, model_dump convierte la instancia de Pydantic en un diccionario estándar de Python, lo que facilita la transformación entre modelos [3:15].

¿Cómo simular un ID autoincremental sin base de datos?

En un entorno real, la base de datos genera el ID con un campo autoincremental. Para practicar sin una base de datos, se utiliza una lista en memoria [3:50]:

python db_customers: list[Customer] = []

  • Se cuenta la cantidad de elementos con len(db_customers) y ese valor se asigna como id.
  • Luego se agrega el customer a la lista con append.
  • Al reiniciar el servidor, la lista se vacía porque todo vive en memoria [4:45].

Es importante recordar que, al usar programación asíncrona, no conviene modificar una variable global de tipo entero desde distintos hilos; por eso la estrategia de contar elementos en la lista resulta más segura para este ejercicio [4:10].

¿Cómo crear un endpoint GET para listar registros?

Siguiendo las convenciones RESTful, el endpoint de listado comparte la misma ruta /customers pero usa el método GET [5:35].

python @app.get("/customers", response_model=list[Customer]) async def list_customers(): return db_customers

  • El response_model se tipa como list[Customer] para indicar que la respuesta es un arreglo de objetos.
  • La función no recibe parámetros porque devuelve todos los registros almacenados.
  • Al probar en la documentación interactiva, primero se crean registros con POST y después se consultan con GET para verificar que el listado incluya cada elemento con su ID asignado [6:20].

¿Qué buenas prácticas se aplican en este flujo?

  • Tipar siempre: cada variable, parámetro y retorno lleva su anotación de tipo, lo que mejora la legibilidad y activa las validaciones automáticas [1:50].
  • Separar modelos públicos de modelos internos: nunca expongas campos que el cliente no necesita enviar o que contengan datos sensibles.
  • Usar response_model: FastAPI filtra la respuesta según el modelo indicado, asegurando que solo se devuelvan los campos definidos.

Ahora que cuentas con los endpoints de creación y listado, el siguiente paso es construir una ruta que reciba un ID como parámetro de ruta y devuelva un solo customer. ¿Cómo manejarías el caso en que el ID no exista en la lista? Comparte tu solución en los comentarios.