Capítulo 4.3: Envío de solicitudes POST/PUT/DELETE
Tiempo de estudio: 1 hora
1. Comandos activos: Del lanzamiento al desguace
Hasta ahora, nuestro Centro de Control de Misión solo solicitaba información (GET
). Ahora aprenderemos a enviar comandos activos:
- POST: "¡Lanzar un nuevo satélite a órbita!"
- PUT: "¡Realizar una modernización completa de los sistemas en la EEI!"
- DELETE: "¡Retirar de órbita la antigua nave
Debris-123
!"
Para ello, utilizaremos fetch
, pero con parámetros adicionales que describen nuestro comando.
💡 Analogía espacial:
Si
GET
es una escucha pasiva del éter radiofónico, entoncesPOST
,PUT
yDELETE
son transmisiones de comandos activas. Para ello, no solo necesitamos especificar la "frecuencia" (URL), sino también el contenido del comando (cuerpo de la solicitud) y el protocolo de comunicación (encabezados).
2. Envío de solicitud POST: Lanzamiento de una nueva nave
Para crear un nuevo recurso, enviamos una solicitud POST
. Lo más importante aquí es pasar el cuerpo (body) de la solicitud con los datos del nuevo objeto.
Paso 1: Añadimos el formulario de creación en index.html
Lo colocaremos después del bloque "Solicitud por ID".
<!-- index.html -->
<hr>
<h2>Lanzar una nueva nave</h2>
<form id="create-ship-form">
<input type="text" id="create-name" placeholder="Nombre" required><br>
<input type="text" id="create-type" placeholder="Tipo" required><br>
<input type="number" id="create-year" placeholder="Año de lanzamiento" required><br>
<input type="text" id="create-status" placeholder="Estado" required><br>
<button type="submit">Lanzar</button>
</form>
<div id="create-status-message"></div>
Paso 2: Añadimos la lógica en app.js
// app.js, al final del archivo
const createShipForm = document.getElementById('create-ship-form');
const createStatusMessage = document.getElementById('create-status-message');
async function createShip(event) {
event.preventDefault();
// 1. Recopilamos los datos del formulario en un objeto
const shipData = {
name: document.getElementById('create-name').value,
type: document.getElementById('create-type').value,
launch_year: parseInt(document.getElementById('create-year').value),
status: document.getElementById('create-status').value
};
try {
createStatusMessage.textContent = 'Enviando comando de lanzamiento...';
// 2. Enviamos la solicitud fetch con parámetros
const response = await fetch(`${API_BASE_URL}/spaceships`, {
method: 'POST', // Indicamos el método
headers: {
'Content-Type': 'application/json' // Le decimos al servidor que enviamos JSON
},
body: JSON.stringify(shipData) // Convertimos el objeto JavaScript en una cadena JSON
});
if (!response.ok) {
// Si el servidor devolvió un error, intentamos leer su cuerpo
const errorData = await response.json();
throw new Error(errorData.detail || `Error del servidor: ${response.status}`);
}
const newShip = await response.json();
createStatusMessage.textContent = `🚀 ¡Lanzamiento exitoso! La nave ha recibido el ID: ${newShip.id}`;
createShipForm.reset(); // Limpiamos el formulario
fetchAndDisplayFleet(); // Actualizamos la lista general de la flota
} catch (error) {
console.error('Error al lanzar la nave:', error);
createStatusMessage.textContent = `🔴 Error: ${error.message}`;
}
}
createShipForm.addEventListener('submit', createShip);
fetch
para POST:
method: 'POST'
: Es obligatorio especificar el método HTTP.headers: { 'Content-Type': 'application/json' }
: Un encabezado críticamente importante. Le informa a nuestro servidor FastAPI que el cuerpo de la solicitud contiene JSON y que debe ser parseado.body: JSON.stringify(shipData)
: No podemos enviar un objeto JavaScript directamente. Necesita ser serializado (convertido) a una cadena JSON.
3. Envío de solicitud DELETE: Desguace de una nave
La solicitud de eliminación es más sencilla: generalmente no necesita un cuerpo, solo la URL con el ID del objeto.
Paso 1: Añadimos un botón "Eliminar" a nuestra lista de naves
Modificaremos la función fetchAndDisplayFleet
en app.js
para que añada un botón de eliminación a cada elemento.
// app.js, dentro de la función fetchAndDisplayFleet
// ...
ships.forEach(ship => {
const listItem = document.createElement('li');
// Añadimos un botón con un atributo data que almacena el ID
listItem.innerHTML = `
<strong>${ship.name} (ID: ${ship.id})</strong><br>
Tipo: ${ship.type} | Año: ${ship.launch_year} | Estado: ${ship.status}<br>
<button class="delete-btn" data-ship-id="${ship.id}">Desguazar nave</button>
`;
fleetList.appendChild(listItem);
});
// ...
Paso 2: Añadimos un controlador para todos los botones "Eliminar" Usamos delegación de eventos: un solo controlador para toda la lista.
// app.js, al final del archivo
async function deleteShip(shipId) {
if (!confirm(`¿Estás seguro de que quieres desguazar la nave con ID ${shipId}? Esta acción es irreversible.`)) {
return;
}
try {
const response = await fetch(`${API_BASE_URL}/spaceships/${shipId}`, {
method: 'DELETE' // Indicamos el método
});
if (!response.ok) {
throw new Error(`No se pudo desguazar la nave. Estado: ${response.status}`);
}
alert(`Nave con ID ${shipId} desguazada con éxito.`);
fetchAndDisplayFleet(); // Actualizamos la lista
} catch (error) {
console.error('Error al desguazar:', error);
alert(`Error: ${error.message}`);
}
}
// Delegación de eventos: escuchamos clics en toda la lista
fleetList.addEventListener('click', (event) => {
// Verificamos si el clic fue en un botón con la clase 'delete-btn'
if (event.target.classList.contains('delete-btn')) {
const shipId = event.target.dataset.shipId; // Obtenemos el ID del atributo data
deleteShip(shipId);
}
});
Paso 3: Añadimos id al modelo Spaceship
Añadimos id en el modelo y la base de datos en el archivo main.py
class Spaceship(BaseModel):
id: int
# El resto del código del modelo...
db_spaceships = {
1: {
"id": 1,
# Datos del elemento 1
},
2: {
"id": 2,
# Datos del elemento 2
},
3: {
"id": 3,
# Datos del elemento 3
}
}
method: 'DELETE'
: Indicamos el método. El cuerpo y los encabezados no son necesarios aquí.confirm()
: Una sencilla ventana de confirmación integrada para evitar eliminar algo importante por error.
4. Envío de solicitud PUT (Tarea independiente)
La implementación de una solicitud PUT
para actualización es muy similar a POST
.
Tu misión, si decides aceptarla:
- Añadir un botón "Modificar" junto al botón "Eliminar" para cada nave.
- Al hacer clic en "Modificar", rellenar el formulario (se puede usar el mismo que para crear) con los datos actuales de la nave.
- Cambiar el texto del botón "Lanzar" a "Actualizar".
- Al enviar el formulario, enviar una solicitud
PUT
a/spaceships/{id}
con el cuerpo completo del objeto. - Después de una actualización exitosa, actualizar la lista de la flota.
Sugerencia: Necesitarás
fetch
conmethod: 'PUT'
, encabezadosContent-Type
ybody
conJSON.stringify()
, exactamente igual que en la solicitudPOST
.
Cuestionario de repaso
🚀 Resumen del capítulo:
¡Su Centro de Control de Misión ahora tiene un conjunto completo de comandos para gestionar la flota!
- ✅ Ha aprendido a enviar solicitudes
POST
con cuerpo y encabezados para crear nuevos recursos. - ✅ Ha implementado solicitudes
DELETE
para dar de baja equipos obsoletos. - ✅ Se le ha asignado la tarea de implementar una solicitud
PUT
, consolidando sus conocimientos.
¡Control total establecido! Pero, ¿qué hacer si la comunicación se interrumpe o el servidor informa un error? En el próximo capítulo, crearemos un sistema centralizado de manejo de errores en el frontend.
📌 Verificación:
- Asegúrese de que el formulario de creación de nuevas naves funcione y que la lista en la página se actualice después de una creación exitosa.
- Verifique que el botón "Dar de baja equipo" funcione, solicite confirmación y elimine la nave de la lista.
- Intente crear una nave con datos no válidos (por ejemplo, con un nombre muy corto) y observe el error que devolverá su servidor FastAPI.
⚠️ Si hay errores:
- Error
422
del servidor: Lo más probable es que los datos que está enviando no pasen la validación de Pydantic. Verifique la consola del navegador —errorData.detail
mostrará en qué campo está el problema.- Error
415 Unsupported Media Type
: Olvidó añadir el encabezado'Content-Type': 'application/json'
.- Los botones de eliminación no funcionan: Verifique si la delegación de eventos funciona correctamente y si está obteniendo
shipId
dedata-ship-id
correctamente.