Aún no tienes acceso a esta clase

Crea una cuenta y continúa viendo este curso

DELETE: borrando michis favoritos

12/20
Recursos

Aportes 12

Preguntas 2

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesión.

Así se va viendo mi proyecto hasta el momento! 😀
.

Aca dejo mi script hasta el momento. Si quieren hacer sugerencias o mejoras con gusto van a ser aceptadas para mejorar el codigo y de paso aprender 😃

Hice mi mejor esfuerzo para que no re-renderice las secciones cada vez que agregaba o quitaba un gatito de favoritos.

HTML:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Michis App</title>
    <link
      rel="stylesheet"
      href="https://necolas.github.io/normalize.css/8.0.1/normalize.css"
    />
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <header>
      <h1>Michis App</h1>
    </header>

    <section id="random-cats">
    </section>
    <button onclick="getRandomCats()">Recargar</button>

    <h2>Michis favoritos</h2>
    <section id="favorite-cats">
    </section>
    <script src="./main.js"></script>
  </body>
</html>

JS

const btn = document.querySelector("button");
const API_KEY = "";

function createArticle(options) {
  const article = document.createElement("article");
  const image = document.createElement("img");
  const button = document.createElement("button");

  article.setAttribute("id", `${options.id}`);
  article.setAttribute("class", `${options.class}`);

  image.setAttribute("src", `${options.url}`);

  button.setAttribute("onClick", `${options.func}('${options.id}')`);
  button.textContent = options.text;

  article.append(image, button);

  return article;
}

async function getRandomCats() {
  const catUrlApi = `https://api.thecatapi.com/v1/images/search?limit=6`;

  const response = await fetch(catUrlApi, {
    cache: "no-cache",
    headers: { "x-api-key": API_KEY },
  });
  const data = await response.json();

  const randomCatsSection = document.getElementById("random-cats");

  while (randomCatsSection.firstChild.className === "random-cat") {
    randomCatsSection.removeChild(randomCatsSection.firstChild);
  }

  const fragment = new DocumentFragment();

  data.forEach((cat) => {
    const article = createArticle({
      id: `${cat.id}`,
      class: "random-cat",
      url: `${cat.url}`,
      text: "Agregar a Favoritos",
      func: "saveCat",
    });
    fragment.append(article);
  });
  randomCatsSection.prepend(fragment);
}

async function getFavoriteCats() {
  const catUrlApi = `https://api.thecatapi.com/v1/favourites`;

  const response = await fetch(catUrlApi, { headers: {"x-api-key": API_KEY}});
  const data = await response.json();

  const randomCatsSection = document.getElementById("favorite-cats");
  
  while (randomCatsSection.lastChild.className === "favorite-cat") {
    randomCatsSection.removeChild(randomCatsSection.lastChild);
  }
  
  const fragment = new DocumentFragment();
  data.forEach((cat) => {
    const article = createArticle({
      id: `${cat.id}`,
      class: "favorite-cat",
      url: `${cat.image.url}`,
      text: "Remover de Favoritos",
      func: "removeCat",
    });
    fragment.append(article);
  });
  randomCatsSection.append(fragment);
}
const removeCat = async (favoriteID) => {
  const remove = await fetch(`https://api.thecatapi.com/v1/favourites/${favoriteID}`, {
    headers: { "x-api-key": API_KEY },
    method: "DELETE",
  });
  const data = await remove.json();
  if(data.message === 'SUCCESS'){
    const catPost = document.getElementById(`${favoriteID}`);
    catPost.remove();
  }
};

const saveCat = async (catID) => {
  const save = await fetch(`https://api.thecatapi.com/v1/favourites/`, {
    headers: {
      "x-api-key": API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ image_id: String(catID) }),
    method: "POST",
  });

  const data = await save.json();
  if(data.message === 'SUCCESS'){
    const randomCatsSection = document.getElementById("favorite-cats");
      const article = createArticle({
        id: `${data.id}`,
        class: "favorite-cat",
        url: `https://cdn2.thecatapi.com/images/${catID}.jpg`,
        text: "Remover de Favoritos",
        func: "removeCat",
      });
      randomCatsSection.append(article);
  }
};

btn.addEventListener("click", getRandomCats);
getRandomCats();
getFavoriteCats();

CSS

header {
    background-color: rgb(9, 84, 170);
    height: 4rem;
    display: flex;
    align-items: center;
    justify-content: center;
}
header h1 {
    margin: 0
}
#random-cats, #favorite-cats{
    width: 100vw;
    margin-top: .5rem;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    justify-items: stretch;
    align-items: stretch;
    gap: 1rem;
 
}
.random-cat, .favorite-cat{
    width: 100%;
    height: minmax(250px,1fr);
    margin: 0 .5rem 0 .5rem;
}

.random-cat img, .favorite-cat img {
    width: calc(100% - 1rem);
    aspect-ratio: 1;
    object-fit: cover;
}
.random-cat button, .favorite-cat button{
    width: calc(100% - 1rem);
}

Asi me quedo mi proyecto, todavía me falta aprender mucho.

HTML

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Gatitos</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <main>
        <h1>API Gatitos</h1>
        <span id="error"></span>
        <section id="random" class="container">
            <h2>Gatitos aleatorios</h2>
            <section id="container_gatitos_random" class="container_gatitos">
                
            </section>
            <button onclick="reloadRandomCats()" class="change">Otros gatitos</button> 
            
        </section>
            
        <section id="section_favorites" class="container" >
            <h2>Gatitos favoritos</h2>
            <section id="favorites" class="container_gatitos">
            </section>
        </section>
        <script src="APIGatito.js"></script>
    </main>
</body>
</html>

CSS

*{
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}
body{
    background-color: rgb(4, 95, 141);
    color: white;
}
main{
    display: flex;
    flex-direction: column;
    width: 1000px;
    margin: 20px auto;
}
h1{
    text-align: center;
    color: orange;
}
.container{
    display: flex;
    flex-direction: column;
}
.container_gatitos{
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
}
.card{
    width: 200px;
    background-color: rgb(182, 182, 182);
    border-radius: 5px;
    margin: 10px;
    display: flex;
    flex-direction: column;
}
.card .card_img{
    width: 200px;
    height: 150px;
    padding: 10px;
    
}
button{
    width: 180px;
    padding: 5px;
    border-radius: 2px;
    border: none;
    display: inline;
    margin: 0 auto;
    box-shadow: 2px 2px 5px rgb(85, 85, 85);
}
.card_button{
    margin-bottom: 10px;
}



button:hover{
    background-color: rgb(86, 86, 228);
}
button:active{
    background-color: rgb(123, 123, 242);
}
.change{
    background-color: rgb(69, 152, 133);
    margin: 10px auto;
    width: 400px;
}

JS

const API_URL_RANDOM = 'https://api.thecatapi.com/v1/images/search?limit=7&api_key=remplazar-api-key';
const API_URL_FAVORITES = 'https://api.thecatapi.com/v1/favourites?api_key=remplazar-api-key';
const API_URL_FAVORITES_DELETE = (id) => `https://api.thecatapi.com/v1/favourites/${id}?api_key=remplazar-api-key`;

const spanError = document.getElementById('error');

async function reloadRandomCats(){
    let response = await fetch(API_URL_RANDOM);
    let data = await response.json();

    if(response.status !== 200){
        spanError.innerHTML = "Hubo un error: "+ response.status;
    }else{
        let element = document.getElementById('container_gatitos_random');
        element.innerHTML = "";
        data.forEach((cat, index) => {
            
            element.innerHTML += `
            <article class="card">
                <img id="imagen${index+1}" src="${cat.url}" class="card_img" alt="Foto de gatitos aleatorios">
                <button id="btn${index + 1}" onclick = "addToFavorites('${cat.id}')" class="card_button">Agregar a favoritos</button> 
            </article>`;
        });
    }
}
reloadRandomCats();
async function reloadFavoriteCats(){
    let response = await fetch(API_URL_FAVORITES);
    let data = await response.json();
    console.log(data);
    if(response.status !== 200){
        spanError.innerHTML = 'Hubo un error '+response.status + data.mesange;
    }else{
        const section = document.getElementById('favorites');
        section.innerHTML = "";
        data.forEach(cat => {
            const article = document.createElement('article');
            const img = document.createElement('img');
            const button = document.createElement('button');
            const buttonText = document.createTextNode('Quitar de favoritos');

            button.className = 'card_button'; 
            button.onclick = () => removeFromFavorites(cat.id);
            button.appendChild(buttonText);
            img.src = cat.image.url;
            img.className = 'card_img';
            article.className = 'card';
            article.appendChild(img);
            article.appendChild(button);
            section.appendChild(article);

        });
    }
}
reloadFavoriteCats();

async function addToFavorites(id){
    console.log('save')
    const response = await fetch(API_URL_FAVORITES,{
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({
            image_id: id
        })
    });
    let data = await response.json();
    if(response.status !== 200){
        spanError.innerHTML = 'Hubo un error '+response.status + data.mesange;
    }else{
       reloadFavoriteCats();
    }
}
async function removeFromFavorites(id){
    const response = await fetch(API_URL_FAVORITES_DELETE(id),{
        method: 'DELETE'
    });
    let data = await response.json();
    if(response.status !== 200){
        spanError.innerHTML = 'Hubo un error '+response.status + data.mesange;
    }else{
        console.log('Eliminado');
        reloadFavoriteCats();
    }
}

Hola ! Para el tema de pronto del Titulo yo estructure el HTML de la siguiente forma:

<div class="miches">
            <h2 class="miches_title"> Favorite Miches</h2>
            <section id="favoriteMichis" class="cards-container">
            </section>
        </div>

Y en el JS lo que hice fue usar un replace Children.

const loadFavouriteMichis = async () => {
    try {
        const section = document.getElementById('favoriteMichis')
        //el fragment se usa para crear un nuevo Objet
        const fragment = new DocumentFragment();
        const res = await fetch(API_URL_FAVOURITES);
        const data = await res.json();

        if ( res.status  !== 200 ){
            spanError.innerText = "Hubo un error: " + res.status + " "  + data.message
        }else{
            data.forEach(michi => {
                const  article  = document.createElement('article')
                const  img  = document.createElement('img')
                const  btn  = document.createElement('button')
                const  btnText  = document.createTextNode('Remove favorite')
                
                img.src = michi.image.url;
                section.appendChild(article);
                article.appendChild(img);
                article.appendChild(btn);
                article.classList.add('card-michi')
                btn.classList.add('button')
                btn.appendChild(btnText);
                btn.addEventListener('click', () => deleteFavouriteMichi(michi.id))
                fragment.appendChild(article)
            })
            section.replaceChildren(fragment)
        }
        
    } catch (error) {
        console.log(error);
    }
}

No se si esto es bueno para performance. Si alguien aqui con más experiencia me puede decir soy todo ojos u oidios jaja :3

Mis constantes de URL:

const API_KEY = 'tuAPIKey'

const BASE_URL = "https://api.thecatapi.com/v1/"
const URL_RANDOM = `${BASE_URL}images/search?limit=2`
const URL_FAVOURITES = `${BASE_URL}favourites?api_key=${API_KEY}`
const URL_FAVOURITES_DELETE = (id) => `${BASE_URL}favourites/${id}?api_key=${API_KEY}````

Hola compañeros quiero compartir el código que hice en base al código del profesor Juan, pero la diferencia es que lo hice más modular, implementé POO, lo escribí con Typescript y lo optimicé un poco.

La manera en que lo optimicé consiste en que el código de ejemplo agrega todos los favoritos o los elimina a todos cada que se elimina uno o se agrega uno. Y pues para optimizarlo tuve que solo seleccionar cual eliminar o agregar a favoritos e hizo que sea más rápido el proyecto.

Modularicé el código haciendo que hayan una clase LoadCatImage, que se encarga de la lógica; otra llamada CatService, que se encarga de hacer el CRUD; y otras dos que se encargan de manipular el DOM. Una se llama HandleEvents, se encarga de colocar el addEventListener; y la otra se encarga de crear los elementos para el DOM.

Pueden encontrar mi aporte en mi github.

Posdata: Me ayudó el curso profesional de Javascript para crear las clases y los cursos de Nicolas(Typescript).

Muy bueno este curso, en 13:30 lo soluicione creando un

div en html

    <section class="section_fav">
        <h2>Favorite Cat</h2>
        <div id="favoriteMichis" class="section__fav-div">
            <!-- Api de gatos favoritos, images & button delete -->
        </div>
    </section>

en la funcion loadFavourite JS

        const sectionDiv = document.getElementById('favoriteMichis')
        sectionDiv.innerHTML = '';
....

Y asi quedo un css rapido usando display flex, con flex-wrap y gap

Aquí el proyecto un poco más organizado el js y con buenas prácticas con códiga ya refactorizado! Cualquier sugerencia en als respuestas:
.
HTML

<!DOCTYPE html>
<html lang="es">

<head>
    <meta charset="UTF-8">
    <meta name="Description" content="Extended Description">
    <meta name="robots" content="index,follow">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Consumo de APIs</title>
    <script defer src="./app.js"></script>

    <style>
        .container {
            display: flex;
            justify-content: center;
            align-items: center;
            flex-direction: column;
            margin-bottom: 48px;
        }
        img {
            display: block;
            background-repeat: no-repeat;
            background-position: center;
            background-size: cover;
        }
        .random-img {
            width: 400px;
            height: auto;
        }
        button {
            margin: 16px 0;
        }
        .random-cats-sec,
        .favorites-cats-sec {
            padding: 25px 25px;
            border: solid 1px black;
            border-radius: 25px;
        }
        .error-app {
            color: red;
        }
    </style>
</head>

<body>
    <div class="container">
        <h1>Gatos App</h1>

        <span class="error-app" id="error"></span>

        <section class="random-cats-sec" id="random-cats">
            <h2>Gatos Aleatorios</h2>
        </section>
        <button id="reload">Recargar</button>

        <section class="favorites-cats-sec" id="favorites-cats">
            <h2>Gatos Favoritos</h2>
        </section>

    </div>
</body>

</html>

.
JS

const API_KEY = 'tuapikey-tuapikey-tuapikey';

const URL_RANDOM = [
    'https://api.thecatapi.com/v1/images/search',
    '?',
    'limit=2',
    `&api_key=${API_KEY}`, // Aquí es necesaria una api key de acuerdo a la documentación
].join('');

const URL_FAVORITES = [
    'https://api.thecatapi.com/v1/favourites',
    '?',
    // 'limit=2',
    `&api_key=${API_KEY}`, // Aquí si es necesaria una api key de acuerdo a la documentación
].join('');

const URL_FAVORITES_ID = (id) => (
    [
        `https://api.thecatapi.com/v1/favourites/${id}`,
        '?',
        `api_key=${API_KEY}`, // Aquí si es necesaria una api key de acuerdo a la documentación
    ].join('')
);

const randomCats = document.querySelector('#random-cats');
const favoritesCats = document.querySelector('#favorites-cats');
const buttonReload = document.querySelector('#reload');
const errorNode = document.querySelector('#error');

// Función para adicionar imagen a una sección
function addImageNode({nodeSection, imageId, imageUrl, attId, buttonText}) {
    const article = document.createElement('article');
    const img = document.createElement('img');
    const button = document.createElement('button');
    img.className = 'random-img';
    img.setAttribute(attId, imageId);
    img.src = imageUrl;
    const btnText = document.createTextNode(buttonText);
    button.appendChild(btnText);
    article.appendChild(img);
    article.appendChild(button);
    nodeSection.appendChild(article);
}

// Cargando imágenes aleatorias
const loadRandomCats = async () => {
    try {
        const response = await fetch(URL_RANDOM); // Por defecto es una solicitud de tipo GET
        const status = response.status;
        if (status !== 200) throw new Error(`Error de petición HTTP en Random: ${status}`);
        const data = await response.json();
        data.forEach(el => {
            addImageNode({
                nodeSection: randomCats,
                imageId: el.id,
                imageUrl: el.url,
                attId: 'data-imageid',
                buttonText: 'Guardar Favorito',
            });
        });
    } catch (error) {
        errorNode.innerText = `Error: ${error.message}`;
    }
};

// Cargando imágenes de favoritos
const loadFavoritesCats = async () => {
    try {
        const response = await fetch(URL_FAVORITES); // Por defecto es una solicitud de tipo GET
        const status = response.status;
        if (status !== 200) throw new Error(`Error de petición HTTP en Favoritos: ${status}`);
        const data = await response.json();
        data.forEach(el => {
            addImageNode({
                nodeSection: favoritesCats,
                imageId: el.id, // no es id de la imagen sino id de favorito
                imageUrl: el.image.url,
                attId: 'data-id',
                buttonText: 'Eliminar Favorito',
            });
        });
    } catch (error) {
        errorNode.innerText = `Error: ${error.message}`;
    }
};

// Salvando imagenes a favoritos
const saveFavoritesCat = async (id) => {
    try {
        const querystring = {
            image_id: id,
        }
        const options = {
            method: 'POST',
            headers: { 'content-type': 'application/json' }, // tipo de archivo de la solicitud
            body: JSON.stringify(querystring),
        }
        const response = await fetch(URL_FAVORITES, options);
        const status = response.status;
        if (status !== 200) throw new Error(`Error de petición POST HTTP en Favoritos: ${status}`);
        const data = await response.json();
        return data.id;
    } catch (error) {
        errorNode.innerText = `Error: ${error.message}`;
    }
};

// Eliminando imágenes de favoritos
const deleteFavoritesCat = async (id) => {
    try {
        const options = { method: 'DELETE' }
        const response = await fetch(URL_FAVORITES_ID(id), options);
        const status = response.status;
        if (status !== 200) throw new Error(`Error de petición DELETE HTTP en Favoritos: ${status}`);
    } catch (error) {
        errorNode.innerText = `Error: ${error.message}`;
    }
}

// Evento recargar imagenes random
buttonReload.addEventListener('click', () => {
    while (randomCats.lastChild.nodeName === 'ARTICLE') {
        randomCats.lastChild.remove();
    }
    loadRandomCats();
});

// Evento guardar imagen en favorito
randomCats.addEventListener('click', (e) => {
    const target = e.target;
    if (target && target.nodeName === 'BUTTON') {
        const img = target.previousSibling;
        const imageId = img.dataset.imageid;
        saveFavoritesCat(imageId).then((id) => {
            alert('Hemos guardado la imagen a favoritos!');
            addImageNode({
                nodeSection: favoritesCats,
                imageId: id, // no es id de la imagen sino id de favorito
                imageUrl: img.src,
                attId: 'data-id',
                buttonText: 'Eliminar Favorito',
            });
        });
    }
});

// Evento eliminar imágenes de favorito
favoritesCats.addEventListener('click', (e) => {
    const target = e.target;
    if (target && target.nodeName === 'BUTTON') {
        const img = target.previousSibling;
        const id = img.dataset.id;
        deleteFavoritesCat(id);
        alert('Hemos eliminado la imagen a favoritos!');
        const parent = target.parentNode;
        if (parent.nodeName === 'ARTICLE') parent.remove();
    }
});

loadRandomCats();

loadFavoritesCats();

// Buenas prácticas
// El deber ser es que debamos verificar si el nodo existe en el html para crear los eventos
// También no conformarse con el evento click, y colocar todos los posibles

/*
const headerEvents = ['touchend', 'touchstart', 'touchmove', 'click'];

if (randomCats) {
    headerEvents.forEach(event => {
        favoritesCats.addEventListener(event, () => console.log('El funcionamieno del evento'));
    });
}

if(randomCats) loadRandomCats();
if(favoritesCats) loadFavoritesCats();
*/

Mi código

const API_URL_RANDOM = 'https://api.thecatapi.com/v1/images/search?limit=2&' //url que nos da imagenes randoms

const API_URL_getFavourites = 'https://api.thecatapi.com/v1/favourites' //url de nuestras imagenes favs
 
const API_KEY = '?api_key=f8bba90a-ad5e-480b-834b-bb39c63aaec6' //api key para hacer solicitudes http

const imageRandom = document.getElementById('image1')

const secondImageRandom = document.getElementById('image2')

const refreshButton = document.getElementById('refresh')

const deleteAllMichisButton = document.getElementById('delete')

const sectionFavouritesMichis = document.getElementById('favoritesMichis')

//función que devuelve gatos aleatorios 
const fetch_api = async () => {

    const request = await fetch(`${API_URL_RANDOM}${API_KEY}`) //url de imagenes randoms
    const response = await request.json()
    console.log('random')
    console.log(response)
    const img = document.querySelectorAll('.img')

    //por cada imagen que coincida con el query selector le asignaremos la imagen que nos sale de la API 
    img.forEach((image, index) => {

        image.src = response[index].url
    })

    //ejecutamos una función al hacer click que retorna la función saveFavouriteMichi() y pasamos el argumento que recibe que es el id del gato, en este caso la propia API en su documentación dice que para guardar un gato en favoritos debemos enviar su id
    imageRandom.onclick = () => saveFavouritesMichi(response[0].id) 
    secondImageRandom.onclick = () => saveFavouritesMichi(response[1].id)
}


//función que nos devuelve los favoritos que nosotros determinamos
const fetch_api_favourites = async () => {

    const error = document.querySelector('#error')
    const request = await fetch(`${API_URL_getFavourites}${API_KEY}`) //concatenamos la ruta
    const response = await request.json() //convertimos a un json manipulable, objeto
    
    try {
        
        console.log('favs')
        console.log(response)
        sectionFavouritesMichis.innerHTML = " "
        //creamos HTML por cada michi favorito y configuramos sus atributos para que salga la imagen del Michi fav
        response.forEach(michi => {

            const article = document.createElement('article')

            const br = document.createElement('br')
            const br2 = document.createElement('br')

            const img = document.createElement('img')
            img.src = michi.image.url
            img.setAttribute("width", 200)
            img.setAttribute("height", 200)


            const btnDelete = document.createElement('button')
            btnDelete.textContent = "Sacar al michi de favoritos"
            try {

                btnDelete.onclick = () => deleteMichiFromFavourites(michi.id)
                

            } catch(err) {

                console.log(err.message)
            }

            article.id = michi.id
            article.appendChild(img)
            article.appendChild(br)
            article.appendChild(br2)
            article.appendChild(btnDelete)
            article.classList.add('article')
            sectionFavouritesMichis.appendChild(article)
            
        })

    } catch(err) {

        error.classList.remove('none')
        error.classList.add('alert', 'alert-danger')
        error.textContent = `Error ${request.status}. ${err.message}`//pasamos el status code y el mensaje
    }
}


//función para enviar el id a la URL de favoritos
const saveFavouritesMichi = async (id) => {

    const saveFavourites = await fetch(API_URL_getFavourites + API_KEY, {

        method: 'POST', //declaración del metodo http
        headers: {

            'Content-Type': 'application/json' //declaramos el tipo de dato que enviaremos
        },

        //convertimos a json el dato que enviaremos ya que no sabemos si podrá leer javascript puro
        body: JSON.stringify({

            image_id: id //id de la imagen del cat que recibimos por parámetro
        })   
    })  

    console.log("Michi guardado en favoritos")
    console.log(saveFavourites) //debugeamos que nos tira la api favourites, que es un arreglo en si
    fetch_api_favourites()

}

//función para eliminar un michi que recibe como parámetro el id para pasar a la url tb. En otras APIS hubiera sido necesario enviar los headers y bodys como el POST pero esta API esta optimizada para solo pasarla por la url, ya que al parecer el backend ya lo hace todo, es decir, envia como body al id que viene de la url y los headers ya están configurados como JSON   
const deleteMichiFromFavourites = async id => {

    const request = await fetch(`${API_URL_getFavourites}/${id}/${API_KEY}`, {

        method: 'DELETE'
    })

    if(request.status === 200) {

        const article = document.getElementById(id)
        article.remove()

    } else {

        //window.alert(`You have a mistake: ${request.status}`)
    }

    console.log(request)
}

//borra todos los michis
const deleteAllMichis = async () => {

    const request = await fetch(`${API_URL_getFavourites}/${API_KEY}`)
    const data = await request.json()

    try{

        data.forEach( async michi => {

            const request = await fetch(`${API_URL_getFavourites}/${michi.id}/${API_KEY}`, {

                method: 'DELETE'
            })

            if(request.status === 200) {

                sectionFavouritesMichis.innerHTML = " "

            } else {

                window.alert(request.status)
            }
        })

    } catch(error) {

        window.alert(error)
    }
}
 

window.addEventListener('DOMContentLoaded', () => {

    window.addEventListener('load', fetch_api)
    window.addEventListener('load', fetch_api_favourites)
    refreshButton.addEventListener('click', fetch_api)
    deleteAllMichisButton.addEventListener('click', deleteAllMichis)

    // imageRandom.addEventListener('click', fetch_api_favourites)
    // secondImageRandom.addEventListener('click', fetch_api_favourites)
    //esto no funciona porque al parecer cuando damos click y la función se ejecuta, el envío de datos aún no terminó por lo que no se renderiza al instante que damos click, en su lugar, llamamos a la función de mostrar los favs una vez que terminó la función de agregar a favoritos

})

Esta es mi funcion para cargar favoritos.

async function loadFavouritesCats() {    
    const res = await fetch(API_URL_FAVOURITES);
    const data = await res.json();

    console.log('loadFavouritesCats()' , data);

    if(res.status !== 200) {
        spanE.innerHTML = 'Hubo un error, nigg ' + res.status + data.message; 
    }

    const favoritesContainer = document.getElementById('favoritesWrap');
    const favoritesArr = document.getElementById('favoriteCatsButton-span');
    favoritesArr.innerText = `${data.length}`;

    // console.log(data.length)
    favoritesContainer.innerHTML = " ";
    data.forEach(element => {
        favoritesContainer.innerHTML += `
        <picture>
            <img src=${element.image.url} id="img1" alt="">
            <button onclick = deleteCat(${element.id}) title="Delete cat">💨</button>
        </picture>      
        `
    });


}

Increible lo bien que esta explicando cada concepto, al final siempre termino entendiendo todo lo que esta explicando, me gusto mucho la clase!

De lejos juan david castro es el mejor profesor de la escuela desarrollo web no solo explica el porque de las cosas paso a paso sino que tambien explica los errores que se pueden cometer y como solucionarlos