Moduł11 - Zajęcia 21 - CRUD

CRUD

Do interakcji z zasobami backendu używane są najczęściej cztery operacje: tworzenie (create) odczytywanie (read), aktualizowanie (update) i usuwanie (delete). Dla każdego z nich zdefiniowana jest standardowa metoda HTTP.

Method Description
POST Operacja create - tworzenie nowego zasobu.
GET Operacja read - pobieranie zestawu zasobów lub jednego zasobu według identyfikatora.
PUT i PATCH Operacja update - aktualizacja zasobu według identyfikatora.
DELETE Operacja delete - usuwanie zasobu według identyfikatora

Pokażemy przykładowe żądania do JSONPlaceholder API, które udostępnia kolekcję fałszywych postów w zasobie /posts, reprezentowanych jako obiekty z id, author i body.

Odczytywanie

Metoda HTTP GET służy do pobierania istniejących danych. Metoda fetch() powinna wysłać żądanie GET bez treści do serwera. Backend po otrzymaniu żądania przetworzy je w odpowiedzi i zwróci niezbędne zasoby.

Zróbmy tablicę wszystkich postów. Aby to zrobić, zapoznaj się z zasobem /posts opisanym w dokumentacji backendu. Metoda fetch() domyślnie tworzy żądanie GET, więc nie jest konieczne definiowanie metody żądania.

Przykład -------------------------------

const btn_s1a2 = document.querySelector('button#btn-s1a2');
const postsList_s1a2 = document.querySelector('ul#user-list-s1a2');

btn_s1a2.addEventListener('click', ev => {
  fetch('https://jsonplaceholder.typicode.com/posts')
    .then(response => response.json())
    .then(posts => {
      renderPosts_s1a2(posts);
    })
    .catch(error => Notify.failure(`${error}`, optionsNotify));
});

function renderPosts_s1a2(posts) {
  postsList_s1a2.innerHTML = null;
  const markup = posts
    .map(({ id, title, body, userId }, index) => {
      return `<li >
          <h2 class="post-title">${index + 1}. ${title.slice(0, 30)}</h2>
          <p><b>Post id</b>: ${id}</p>
          <p><b>Author id</b>: ${userId}</p>
          <p>${body}</p>
        </li>`;
    })
    .join('');
  postsList_s1a2.innerHTML = markup;
}
            
fetch("https://jsonplaceholder.typicode.com/posts")
  .then(response => response.json())
  .then(posts => console.log(posts))
  .catch(error => console.log(error));
          

    PrzykładEND -------------------------------

    Tworzenie

    Metoda POST służy do dodawania nowego zasobu. Metoda fetch() powinna wysłać do serwera żądanie POST, w ciele - body którego będzie obiekt z polami author i body, identyfikator zostanie automatycznie utworzony przez bazę danych. Wynikiem takiego żądania będzie obiekt który został dodany do bazy danych.

    Przykład -------------------------------

    const form_s1a3 = document.querySelector('form#form-s1a3');
    const postsList_s1a3 = document.querySelector('ul#user-list-s1a3');
    
    function getOptionsToUrl(author, body) {
      const postToAdd = {
        author,
        body,
      };
    
      const options = {
        method: 'POST',
        body: JSON.stringify(postToAdd),
        headers: {
          'Content-Type': 'application/json; charset=UTF-8',
        },
      };
      return options;
    }
    
    form_s1a3.addEventListener('submit', event => {
      event.preventDefault();
      const {
        elements: { input_author_s1a3, input_body_s1a3 },
      } = event.currentTarget;
      console.log(input_author_s1a3.value, input_body_s1a3.value);
      const options = getOptionsToUrl(
        input_author_s1a3.value,
        input_body_s1a3.value
      );
      fetch('https://jsonplaceholder.typicode.com/posts', options)
        .then(response => response.json())
        .then(post => {
          console.log(post);
          renderPosts_s1a3(post);
        })
        .catch(error => Notify.failure(`${error}`, optionsNotify));
    });
    
    function renderPosts_s1a3(postAdd) {
      postsList_s1a3.innerHTML = null;
      const { author, body, id } = postAdd;
      const markup = `<li >
              <p><b>Method POST:</b></p>
              <p><b>Post author</b>: ${author}</p>
              <p><b>Post body</b>: ${body}</p>
              <p><b>Author id</b>: ${id}</p>
            </li>`;
      postsList_s1a3.innerHTML = markup;
    }
                

      PrzykładEND -------------------------------

      Żądanie utworzenia posta wysyłamy, uzyskując dostęp do zasobu /posts, ale w ustawieniach metody fetch() zmieniamy metodę HTTP na POST. Tak więc backend wie, że nie musi odczytywać istniejącego, ale powinien stworzyć nowy zasób w tej kolekcji.

      Treść żądania musi być ciągiem, ponieważ protokół HTTP przesyła wszystko jako tekst. Podczas przesyłania złożonych typów danych należy je przekonwertować na ciąg znaków za pomocą metody JSON.stringify(). Nie zapomnij podać nagłówka Content-Type, który określa dla backendu typ przesyłanych danych.

      W odpowiedzi, jeśli wszystko jest w porządku, otrzymamy JSON z dodanym id. Identyfikator będzie unikalny dla każdego obiektu.

                  {
                    "id": 1,
                    "author": "Mango",
                    "content": "CRUD is awesome"
                  }
                

      S1A4: Aktualizowanie

      Obie metody PUT i PATCH służą do aktualizacji istniejących danych. Której metody użyć, zostanie opisane w dokumentacji backendu. Metoda fetch() musi wysłać do serwera żądanie, w treści którego należy wskazać obiekt z polami do zmiany. Ścieżka wskazuje, którą kolekcję i który element chcemy zaktualizować. Backend po otrzymaniu żądania przetworzy je i zwróci w odpowiedzi zaktualizowany zasób.

      Przykład -------------------------------

      const form_s1a4 = document.querySelector('form#form-s1a4');
      const postsList_s1a4 = document.querySelector('ul#user-list-s1a4');
      
      function getOptionsToUrl_s1a4(id, body) {
        const postToAdd = {
          id,
          body,
        };
      
        const options = {
          method: 'PATCH',
          body: JSON.stringify(postToAdd),
          headers: {
            'Content-Type': 'application/json; charset=UTF-8',
          },
        };
        return options;
      }
      
      form_s1a4.addEventListener('submit', event => {
        event.preventDefault();
        const {
          elements: { input_id_s1a4, input_body_s1a4 },
        } = event.currentTarget;
        console.log(input_id_s1a4.value, input_body_s1a4.value);
        const options = getOptionsToUrl_s1a4(
          input_id_s1a4.value,
          input_body_s1a4.value
        );
        fetch(
          `https://jsonplaceholder.typicode.com/posts/${input_id_s1a4.value}`,
          options
        )
          .then(response => response.json())
          .then(post => {
            console.log(post);
            renderPosts_s1a4(post);
          })
          .catch(error => Notify.failure(`${error}`, optionsNotify));
      });
      
      function renderPosts_s1a4(patchAdd) {
        const { id, title, body } = patchAdd;
        const markup = `<li >
                <p><b>Method POST:</b></p>
                <p><b>Post id</b>: ${id}</p>
                <p><b>Post title</b>: ${title}</p>
                <p><b>Post body</b>: ${body}</p>
              </li>`;
        postsList_s1a4.innerHTML = markup;
      }
                  

        PrzykładEND -------------------------------

        W odpowiedzi, jeśli wszystko będzie dobrze, otrzymamy zaktualizowany obiekt.

                    {
                      id: 1,
                      author: 'Mango',
                      content: 'CRUD is really awesome',
                    }
                  

        Wedle konwencji, metoda PATCH zastępuje tylko wartości przekazane w treści żądania właściwości w istniejącym zasobie - czyli aktualizuje go częściowo. Metoda PUT z kolei całkowicie zastępuje zasób lub tworzy go, jeśli jeszcze nie istnieje.

        S1A5: Usuwanie

        Metoda DELETE służy do usuwania istniejących danych. Metoda fetch() powinna wysłać do serwera żądanie DELETE bez treści. Ścieżka wskazuje, w której kolekcji i który element chcemy usunąć. Backend po otrzymaniu żądania przetworzy je, usunie zasób z kolekcji i zwróci status wyniku w odpowiedzi - czasami zobaczymy tu status 204 czyli operacja przebiegła pomyślnie, ale nie ma dla nas żadnej odpowiedzi (co ma sens ponieważ zasób już nie istnieje)

        Przykład -------------------------------

        const form_s1a5 = document.querySelector('form#form-s1a5');
        const postsList_s1a5 = document.querySelector('ul#user-list-s1a5');
        
        function getOptionsToUrl_s1a5(id) {
          const postToDelete = {
            id,
          };
        
          const options = {
            method: 'DELETE',
            body: JSON.stringify(postToDelete),
            headers: {
              'Content-Type': 'application/json; charset=UTF-8',
            },
          };
          return options;
        }
        
        form_s1a5.addEventListener('submit', event => {
          event.preventDefault();
          const {
            elements: { input_id_s1a5 },
          } = event.currentTarget;
          console.log(input_id_s1a5.value);
          const options = getOptionsToUrl_s1a5(input_id_s1a5.value);
          fetch(
            `https://jsonplaceholder.typicode.com/posts/${input_id_s1a5.value}`,
            options
          )
            .then(response => response.json())
            .then(post => {
              console.log(post);
              renderPosts_s1a5(post);
            })
            .catch(error => Notify.failure(`${error}`, optionsNotify));
        });
        
        function renderPosts_s1a5(postDelete) {
          const { id, title, body } = postDelete;
          const markup = `<li >
                  <p><b>Method POST:</b></p>
                  <p><b>Post id</b>: ${id}</p>
                  <p><b>Post title</b>: ${title}</p>
                  <p><b>Post body</b>: ${body}</p>
                </li>`;
          postsList_s1a5.innerHTML = markup;
        }
                    

          PrzykładEND -------------------------------