Najważniejsze zagadnienia, które musisz znać
5 godz. 53 min · JavaScript · Full-stack i Programowanie
Adam GospodarczykPrzesyłanie oraz transformacja danych to jeden z najważniejszych elementów działania każdej aplikacji. Dobre zrozumienie tego w jaki sposób odbywa się ten proces, daje równocześnie lepsze zrozumienie całej struktury oraz podejmowania decyzji projektowych. W tym kursie znajdziesz lekcje, które pozwolą Ci uporządkować wiedzę na temat protokołu HTTP oraz formatów wymiany danych.
REST oraz GraphQL to nadal nieodłączone zagadnienia tematu wymiany informacji w kontekście aplikacji webowych. Pomimo tego, że ich koncepcje zwykle są bardzo dobrze opisane w różnych źródłach, tak ich wykorzystanie "na produkcji" potrafi sprawić problemy. W szczególności gdy weźmiemy pod uwagę, wyjątki oraz fakt, że nie każda aplikacja będzie wymagać implementacji REST czy GraphQL w 100%. W tym kursie znajdziesz wiele praktycznych wskazówek związanych z pracą z REST API oraz wprowadzenie do pracy z GraphQL.
Niektóre błędy w aplikacji są bardzo pożądane, w szczególności gdy jasno informują użytkownika o tym, co się wydarzyło oraz o tym, co może z tym zrobić. Pomimo tego, że w wielu przypadkach opracowanie komunikatów leży w rękach osoby odpowiedzialnej za copywriting (i/lub ux writing), tak sama implementacja tych komunikatów odbywa się po stronie kodu. Nie rzadko okazuje się, że poprawna i skuteczna walidacja danych oraz obsługa błędów nie jest oczywista w "produkcyjnych" sytuacjach. W tym kursie znajdziesz najczęściej spotykane problemy oraz sposoby ich rozwiązania.
Bazy danych są praktycznie nieodłączną częścią każdej aplikacji. W roli full-stacka prędzej czy później przyjdzie Ci z nimi pracować a nie rzadko nawet projektować oraz rozbudowywać ich struktury. W tym kursie znajdziesz informacje o bazach danych, które pozwolą Ci je zrozumieć oraz od razu poznać użyteczne techniki, które pomogą Ci w pracy z nimi, zarówno na produkcji jak i w środowisku lokalnym czy testowym.
Nie wszystkie informacje w aplikacji są publiczne. Niektóre wprost nie mogą takie być. Z tego powodu niezbędne są sposoby na to aby poznać a potem weryfikować tożsamość użytkownika, by na jej podstawie przydzielać mu dostęp wyłącznie do zasobów, do których odczytywania posiada uprawnienia. Istnieje wiele technik, które umożliwiają zarządzanie dostępem do danych i w tym kursie poznasz najważniejsze z nich. Dowiesz się nie tylko w jaki sposób zabezpieczać dane ale poznasz też najpopularniejsze sposoby na to aby te zabezpieczenia łamać i obchodzić. Mając świadomość takiej możliwości, będziesz w stanie lepiej podejmować decyzje o tym jak projektować dostęp do informacji.
Istnieją problemy charakterystyczne dla niemal każdej aplikacji. W związku z ich powszechnością, istnieje też szereg rozwiązań lub sposobów na to, aby możliwie zredukować ich następstwa. Na przestrzeni lekcji tego kursu zobaczysz wiele przykładów, które pomogą Ci w codziennej pracy a niekiedy nawet, pozwolą uniknąć błędów, których konsekwencje nie zawsze występują natychmiast.
Kurs powstał z myślą o osobach, które chcą rozwijać się w roli full-stack web developera / developerki, przeprowadzając przez najważniejsze zagadnienia z obszaru wymiany, transformacji oraz przechowywania informacji w aplikacji oraz pomiędzy aplikacjami. Jeżeli potrzebujesz zobaczyć szeroką perspektywę tego, w jaki sposób dane przepływają pomiędzy różnymi częściami aplikacji, lub ugruntować swoją wiedzę na ten temat, to ten kurs jest miejscem, którego szukasz.
W tej lekcji podsumujemy sobie
najważniejsze wątki w temacie komunikacji z wykorzystaniem protokołu HTTP.
Przede wszystkim do wykonywania zapytań.
Aktualnie najlepiej jest wykorzystywać PHP oraz inną rzecz.
Warto jednak tutaj podkreślić, że od
wersji 18 noda nie ma potrzeby wykorzystywania zewnętrznych paczek ze
względu na to, że pojawia się już tam natywne wsparcie dla PHP.
Idąc dalej w związku z tym, że przyjdzie
nam pracować zarówno z naszym API oraz też API zewnętrznych usług, warto w zależności
od potrzeb przygotować sobie parser błędów.
Jest to nic innego jak funkcja, która umożliwi Ci łatwe zarządzanie komunikatami
o błędach, tak aby były jak najbardziej przyjazne zarówno dla użytkownika, jaki na
potrzeby logowania różnych zdarzeń w Twojej aplikacji.
Podobnie też możesz stosować różnego rodzaju helper, których celem przede
wszystkim będzie to, aby ułatwić samą komunikację poprzez realizowanie w jednym
miejscu powtarzalnych zadań, takich jak chociażby jakieś transformacje danych,
które przychodzą do Twojej aplikacji lub tych, które są z niej wysyłane.
No i ostatecznie na etat.
Parser Helper warto też wykorzystywać, aby przygotować sobie globalne handler.
Oznacza to mniej więcej tyle, aby zarówno
po stronie frontendu, jak i backendu przygotować sobie centralne miejsce
służące do zarządzania tym, w jaki sposób obsługujemy błędy, wyświetlamy je
użytkownikowi czy chociażby logujemy po stronie serwera.
Zaprojektowanie takiego globalnego
Chandlera nie jest do końca prostym zadaniem, aczkolwiek w wielu przypadkach
frameworki, z których będziemy korzystać mocno będą nam to ułatwiać.
Niebawem będziemy zajmować się tym po stronie backendu.
Aczkolwiek miej na uwadze fakt, że podobne
techniki można również stosować po stronie frontendu i jeżeli już jesteśmy przy
technikach, to dobrze jest zwrócić uwagę jeszcze na filtry oraz tzw.
inter capture.
Jest to nic innego jak specjalne funkcje, które wykonywane są pomiędzy jakimiś
akcjami po to, aby przetransformować dane, bądź też odpowiednio je przefiltrować.
Zaraz zobaczymy jak
możemy zastosować w przypadku frontendu, natomiast szerzej po stronie backendu
będziemy sobie jeszcze więcej mówić przy okazji funkcji typu middleware.
No i ostatecznie jeżeli chodzi o techniki związane zarówno z pracą w sferze API,
zasadami helper global, dilerami czy filtrami sektorami.
Myślę, że warto sięgać po inspiracje dostępne na GitHubie.
Mam tutaj na myśli to co już pokazywałem, czyli podglądanie dokumentacji API oraz
tego w jaki sposób zwracane są informacje o błędach.
I przykładowo jeżeli przejdziemy do API Digital Option to widzimy bardzo
dobrze nawet na przykładzie pierwszego z brzegu Endpoint.
To, że mamy tutaj obsługę metody GET,
która zwraca odpowiedzi w postaci 200 401, 429 czyli jak widzisz mamy tutaj do
czynienia z limitami REST, oczywiście z ewentualnymi błędami serwera.
Co więcej, oprócz tego mamy nie tylko
informacje na temat tego, jak wygląda struktura odpowiedzi, ale również jak
wygląda struktura odpowiedzi w przypadku błędów.
Zauważ, że Digital 8 stosuje nie tylko statusy błędów, ale również zamiast kodów
błędów stosuje identyfikatory, które wprost wskazują na konkretny rodzaj błędu.
Zachowanie takiej spójności na przestrzeni całego API, które w tym przypadku jest
niesamowicie rozbudowane, wymaga właśnie zastosowania technik.
Takie jak chociażby globalne handle, które
właśnie dbają chociażby o to, aby nie było tutaj żadnych literówek.
Zatem proponuję, abyśmy przeszli teraz do
kodu i zobaczyli jak to może wyglądać w praktyce.
Zacznijmy więc od prostego przykładu.
Wyślemy sobie zapytanie na adres URL
i tutaj, aby wykonać zapytanie musimy tylko ustawić odpowiednie nagłówki, czyli
metoda post i nagłówki content type oraz autoryzację.
To jest tracie.
Możemy pobrać sobie te informacje, a następnie wyświetlić w konsoli.
Jeżeli teraz uruchomię sobie serwer,
oczywiście wszystko będzie tutaj w porządku.
Natomiast naturalnie jest to klasyczny przykład Happy path, gdzie wystarczy tak
naprawdę jeden mały problem, aby nasza aplikacja przestała działać.
Poza tym nie tylko obsługa błędów jest
tutaj problemem, ale za każdym razem, gdy będziemy chcieli wykonać kolejne zapytanie
do tego API, ciągle będziemy musieli się powtarzać z nagłówkami oraz kluczem API.
Zdecydowanie lepiej więc byłoby przygotować jedną funkcję, która będzie
odpowiedzialna za kontakt z naszym API i to wewnątrz niej będziemy konfigurować te
podstawowe ustawienia, a cała reszta będzie zmienna.
Aby to zrobić, wykorzystamy sobie teraz jedną z technik, aby to osiągnąć
jest wyciągnięcie sobie referencji do Original Fetch, które wykorzystamy do
tego, aby nadpisać metodę fetch dostępną w przeglądarce.
Szczerze mówiąc, jeżeli chodzi o takie
nadpisywanie domyślnych zachowań przeglądarki, ja raczej jestem temu
przeciwny i wydaje mi się, że dobrym pomysłem byłoby tutaj sięgnięcie po inną
nazwę niż fetch, a sam fetch zostawić w spokoju.
Tak czy inaczej nasza funkcja będzie oczywiście zwracała obietnice i będziemy
mogli przekazać do niej dowolną liczbę argumentów, które będziemy tutaj wyciągać.
Następnie odwołamy się do naszego
oryginalnego kwacha po to, aby wykonać zapytanie na wskazany adres URL i
przekazać tutaj dodatkowe opcje konfiguracji.
W rezultacie to co osiągamy w tym miejscu
to po prostu możliwość modyfikowania zachowania metody fetch dla każdego
zapytania, które będzie realizowane w ramach naszej aplikacji.
W tym przypadku napisaniem, które tutaj stosujemy dotyczy ustawienia nagłówków.
Cała reszta można powiedzieć, że pozostaje taka sama.
No to teraz tak naprawdę jedyne co nas
będzie interesowało to to, aby wykonując zapytanie określić tylko metodę zapytania.
Jeżeli teraz ponownie zajrzymy do naszej aplikacji, to ponownie wszystko powinno
działać i informacje o użytkownikach zostają pobrane pomyślnie.
Oczywiście w produkcyjnej aplikacji pamiętaj proszę o tym, aby klucz API, o
ile nie jest publiczny był przechowywany w zmiennych środowiskowych, a nie
bezpośrednio w dostępnym dla użytkownika kodzie aplikacji.
W każdym razie to, co tutaj zyskaliśmy dotyczy przede wszystkim wygody i
elastyczności oraz braku konieczności powtarzania się w przypadku auto
testowania połączenia i ich podstawowej konfiguracji.
Teraz spróbujemy zastosować sobie filtry,
które umożliwią nam między innymi obsługę błędów.
W tym celu na początek zdefiniujemy sobie
typy opisujące użytkownika oraz naszą odpowiedź.
W przypadku naszego API mamy tutaj do czynienia z danymi bądź też właściwością
message, która pojawia się w przypadku błędów.
No to teraz wystarczy, że wykorzystamy składnie to,
aby wykonać zapytanie na wskazany łapką hook, a następnie odczytać dane i
ewentualnie przechwycić błędy z pomocą w tym przypadku Console loga.
No i teraz możemy wykorzystać tutaj destruktora, a następnie odczytać np.
imię pierwszego użytkownika.
I tutaj jeszcze tylko wykorzystamy sobie type i to teraz jeżeli przejdziemy
ponownie do naszej aplikacji okaże się, że to nasz pierwszy użytkownik.
Za to wygląda na to, że jesteśmy krok dalej.
Ale jeżeli teraz nasze API przestanie
działać, to podobnie razem z nim przestaje działać również nasza aplikacja.
A to oznacza mniej więcej tyle, że nasza obsługa błędów nie do końca działa.
W związku z tym, że nasz błąd dzieje się w
momencie parsowania obiektu wyżej, proponuję, abyśmy wykorzystali tutaj nową
metodę o nazwie Filter Response, która będzie przyjmowała odpowiedź i w
zależności od tego, co w tej odpowiedzi zostanie zwrócone, będzie zwracała albo
nasze dane, albo będzie wyrzucać wyjątek, który zostanie przechwycony w tym bloku.
Zatem przede wszystkim będziemy tutaj potrzebowali generacji innego typu, który
będzie określał to, co zostanie odrzucone w przypadku poprawnej odpowiedzi.
Podobnie też określimy
rodzaj danych, które mogą zostać zwrócone z takiej funkcji.
Zatem teraz mamy do czynienia z funkcją,
do której możemy przekazać typ taki jak np.
tablica użytkowników.
Ona zostanie podstawiona w tym miejscu, a następnie argumentem tej funkcji będzie
odpowiedź, której typ pochodzi bezpośrednio z SHP.
I mamy tutaj dokładnie opisane wszystkie właściwości.
A następnie zwracamy obietnicę, która rozwiązuje się do typu
response type, do którego przekazujemy typ naszych danych.
No i teraz w pierwszej kolejności musimy
sprawdzić, czy nagłówek odpowiedzi z serwera zawiera application gn.
Jeżeli tak, to jesteśmy w stanie te
informacje poprawnie odczytać, ale też musimy sprawdzić, czy samo zapytanie
zostało wykonane poprawnie po stronie serwera.
Jeżeli tak się nie stanie, oznacza to, że
otrzymujemy tutaj błąd, który również zawiera obiekt sam możemy odczytać.
W związku z tym wyrzucamy sobie wyjątek, który zostanie przechwycony w tym miejscu.
W przeciwnym razie mamy do czynienia z sytuacją, w której zapytanie zostało
wykonane poprawnie, a w związku z tym możemy bezpiecznie zwrócić wynik.
No i ostatecznie w sytuacji, gdy
nie zwróci nam poprawnej odpowiedzi, czasami mamy do czynienia z błędem serwera
i również zwracamy wyjątek tutaj w tym przypadku również zostanie tutaj
zalogowany, więc nie ma potrzeby robić tego podwójnie.
Tak czy inaczej teraz możemy wykorzystać tą naszą metodę w taki sposób.
I w rezultacie jeżeli tutaj sobie
podejdziemy to wygląda na to, że nasze typy tutaj poprawnie działają.
A jednocześnie jeżeli po raz kolejny wrócimy do naszej aplikacji, no to okaże
się, że w tym momencie nasz serwer nie działa i mamy oczywiście server error, ale
gdy tylko go aktywuje dane zostaną poprawnie odczytane.
Jednocześnie jeżeli dojdzie np.
do błędu z samym połączeniem i przykładowo
jego autoryzacją, zatem mamy tutaj błędny klucz API.
Zostaniemy o tym również poinformowani poprzez status oraz odpowiednią
informację, którą możemy wyświetlić użytkownikowi.
Zatem w tym wszystkim doszliśmy do momentu, w którym po pierwsze nasze
zapytania obsługiwane są globalnie, w związku z tym nie musimy się powtarzać i
jednocześnie poprzez interpreter mamy kontrolę nad tym, w jaki sposób te
zapytania są wysyłane, a jednocześnie dzięki Filipowi mamy też kontrolę nad tym,
co dzieje się z samym zapytaniem i w rezultacie wszędzie tam, gdzie dojdzie do
błędu związanego z samym pobieraniem informacji z API, jesteśmy w stanie
odpowiednio zareagować i ponownie też nie musimy się.
Do tego, aby obsługiwać te same scenariusze wielokrotnie.
Na samym końcu zostały nam jeszcze LP, których aktualnie nie będę już pokazywał.
Natomiast musisz wiedzieć, że LPT możesz wykorzystać np.
po to, żeby w momencie gdy zapytanie
zostanie zrealizowane poprawnie obsłużyć chociażby sytuacje dotyczące chociażby
notyfikacji użytkownika o tym, że zostały pobrane poprawnie lub też ewentualnie
możesz wykorzystać w tym miejscu AVR do tego aby poprawnie odczytać błędy,
które zapis z różnych powodów mogą przychodzić w formacie, który nie nadaje
się do tego, aby wyświetlić go użytkownikowi.
Ostatecznie mam nadzieję, że koncepcja praktycznej pracy z API oraz projektowania
tych globalnych funkcji obsługujących podobne scenariusze jest dla Ciebie jasna.
I jednocześnie pamiętaj proszę o tym, że podobne strategie wykorzystujemy po
stronie backendu, natomiast tam mówimy bardziej o funkcjach typu middleware, o
których jeszcze niejednokrotnie będziemy mówić teraz.
Dzięki za uwagę i do usłyszenia niebawem.