Creación de endpoints con PUT

Objetivo de esta sección

  • Entender qué es el método PUT y cuándo usarlo.
  • Crear un endpoint que permita actualizar recursos existentes.
  • Aprender a combinar Path Parameters (para identificar un recurso) con Body (JSON) para enviar los nuevos datos.
  • Usar Pydantic para validar los cambios.

Qué es PUT?

PUT es un método HTTP que se utiliza para reemplazar o actualizar un recurso completo. Normalmente se usa junto con un ID que identifica al recurso.

La diferencia con POST:

  • POST: crea un recurso nuevo.
  • PUT: actualiza un recurso existente.

Nota: También existe PATCH, que se usa para actualizaciones parciales. Lo veremos más adelante.

Base de datos simulada

Vamos a trabajar con un ejemplo de productos. Tenemos un modelo Producto y una lista en memoria que simula nuestra base de datos:

from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel, Field
from typing import List, Optional

app = FastAPI(title="API de Productos con PUT")

# --------- Modelo ----------
class Producto(BaseModel):
    codigo: int = Field(gt=0, description="Identificador único")
    descripcion: str = Field(min_length=3, max_length=100)
    precio: float = Field(gt=0, description="Precio mayor que 0")

# Modelo para actualización (permite campos opcionales)
class ProductoUpdate(BaseModel):
    descripcion: Optional[str] = Field(None, min_length=3, max_length=100)
    precio: Optional[float] = Field(None, gt=0)

# --------- Base simulada ----------
productos_db: List[Producto] = [
    Producto(codigo=1, descripcion="Teclado", precio=50.0),
    Producto(codigo=2, descripcion="Mouse", precio=30.0),
]

Endpoint PUT para actualizar

@app.put("/productos/{codigo}", response_model=Producto)
def actualizar_producto(codigo: int, datos: ProductoUpdate):
    # Buscar producto por código
    for producto in productos_db:
        if producto.codigo == codigo:
            # Actualizar solo los campos enviados
            if datos.descripcion is not None:
                producto.descripcion = datos.descripcion
            if datos.precio is not None:
                producto.precio = datos.precio
            return producto

    # Si no se encontró
    raise HTTPException(
        status_code=status.HTTP_404_NOT_FOUND,
        detail="Producto no encontrado"
    )

Explicación paso a paso

  • @app.put("/productos/{codigo}"): el parámetro codigo se recibe en la URL.
  • datos: ProductoUpdate: recibe en el cuerpo un objeto JSON con los campos a modificar.

Dentro de la función:

  • Se busca el producto en la lista productos_db.
  • Si existe, se actualizan los campos que no son None.
  • Si no existe, se devuelve un error 404.
  • La respuesta devuelve el producto actualizado en JSON.

Ejemplos en Swagger UI (/docs)

1) Actualizar descripción

PUT /productos/1
Body:

{
  "descripcion": "Teclado mecánico retroiluminado"
}

# Respuesta:
{
  "codigo": 1,
  "descripcion": "Teclado mecánico retroiluminado",
  "precio": 50.0
}

2) Actualizar precio

PUT /productos/2
Body:

{
  "precio": 35.0
}

# Respuesta:
{
  "codigo": 2,
  "descripcion": "Mouse",
  "precio": 35.0
}

3) Producto inexistente

PUT /productos/99
Body:

{
  "descripcion": "Monitor"
}

# Respuesta:
{"detail": "Producto no encontrado"}

Buenas prácticas con PUT

  • Usar modelos separados: Producto para crear y ProductoUpdate para actualizar (con campos opcionales).
  • Validar ID en path: asegurarse de que el recurso exista antes de actualizar.
  • Status codes correctos: 200 OK (actualización exitosa), 404 Not Found (recurso no existe).
  • PATCH vs PUT: PUT suele reemplazar todo el recurso; PATCH se usa cuando solo actualizamos un campo puntual.

Conclusión

  • PUT sirve para actualizar recursos existentes.
  • Combinamos Path Parameters (ID) + Body JSON (datos a cambiar).
  • FastAPI + Pydantic validan automáticamente la entrada.
  • Con GET + POST + PUT ya podemos leer, crear y modificar recursos en nuestra API.