w Praktyce
10 godz. 44 min · Node.js · Full-stack i Programowanie
Piotr PalarzWeb DeveloperW kursie tym poznasz platformę Node.js od podstaw, aż po bardziej zaawansowane koncepcje. Zaczniemy od omówienia czym jest Node, a także jakie może być zastosowanie tej technologii. Następnie przejdziemy przez proces instalacji i napisanie swojego pierwszego skryptu. Już na samym początku dokładnie omówimy tworzenie własnych modułów, gdyż jest to wiedza niezbędna, by dobrze zrozumieć funkcjonowanie Node. Następnie dowiesz się czym są zdarzenia, jak działa model “publish / subscribe”, a także czym jest “event-driven development”. Chwilę później omówimy pracę z buforami, stream’ami, a także ze standardowym wejściem i wyjściem. Ta wiedza pozwoli Ci zrozumieć jak tworzone aplikacje mogą otrzymywać od użytkownika dane oraz jak je wyświetlają.
Niezwykle ważnym rozdziałem będzie ten, poświęcony pracy z systemem plików. Szczególnie dla dewelopera, pracującego dotychczas z językiem JavaScript wyłącznie w środowisku przeglądarki internetowej. Node pozwala bowiem tworzyć nowe pliki, zapisywać w nich dane oraz odczytywać i usuwać pliki i katalogi. Przy wszystkich tych operacjach, niezwykle ważna jest jednak asynchroniczna natura Node. Dowiesz się jak działa, a także jakie są jej zalety. Na tym etapie nie zabraknie oczywiście praktycznego projektu, którym będzie aplikacja typu CLI (Command Line Interface). Pozwoli nam ona grupowo zmienić nazwy wielu plików, według podanego wzorca.
W kolejnym rozdziale poruszymy fundamentalną dla Node kwestię, mianowicie tworzenie serwerów sieciowych. Zaczniemy od najniższego poziomu, tworząc prosty serwer bazujący na protokole TCP/IP. Wiedza w jaki sposób Node radzi sobie z takim zadaniem, pozwoli później lepiej zrozumieć wyższe warstwy, takie jak np. HTTP czy WebSockets. Chwilę później stworzymy swój pierwszy serwer HTTP, do którego będzie można wysyłać zapytania wprost z przeglądarki internetowej. Omówimy również jak w Node wykorzystać bezpieczne szyfrowanie HTTPS.Na tym etapie będziesz już miał solidną wiedzę jak pracować z Node, jednak zanim przejdziemy dalej, zatrzymamy się, by omówić jak dokładnie działa Node.js, a także jak ta platforma została zbudowana. Dwie dość obszerne lekcje, będą być może jednymi z najważniejszych w tym kursie. Dowiesz się w nich czym jest, a także jak działa jednowątkowa pętla zdarzeń, czym jest proces i wątek, a także zobaczysz kod źródłowy Node.js i wszystkie bloki budulcowe, z jakich Node się składa. To naszym zdaniem niezwykle ważny temat, często jednak pomijany w wielu materiałach. Dzięki dogłębnemu zrozumieniu jednowątkowej natury języka JavaScript i asynchroniczności Node, będziesz mógł tworzyć dużo lepsze aplikacje.
Node.js posiada kilkanaście wbudowanych modułów, z których w dowolnej chwili można skorzystać. Wszystkie jednak dają nam dużą, niskopoziomową kontrolę, ale czasami sporo kodu należy napisać, by zrealizować jakieś zadanie. Jest to jednak świadomy wybór twórców Node, gdyż całą resztę świetnych rozwiązań, dopisuje nieustannie ogólnoświatowa społeczność deweloperów. Zdecydowana większość z modułów ląduje w rejestrze npm. Jest to genialny package manager, który pozwoli nam wyszukiwać, pobierać i aktualizować niezliczoną ilość modułów. Dowiesz się zatem jak korzystać z npm, a chwilę później zaczniemy wykorzystywać zgromadzone tam moduły w dalszej pracy.Jednym z najpopularniejszych modułów jest express.js, który pozwala w bardzo prosty sposób tworzyć serwery HTTP. Grzechem byłoby zatem z niego nie skorzystać. Dowiesz się wszystkiego co niezbędne, by zacząć tworzyć własne aplikacje bazujące na protokole HTTP z użyciem express.js, a także kilku dodatków do tego systemu. Zobaczysz jak routować zapytania, jak korzystać z middleware czy serwować statyczne pliki. Następnie omówimy współpracę z bazą danych MongoDB i z jej wykorzystaniem stworzymy API typu REST. Stworzona aplikacja pozwoli odczytywać dane z bazy i przesyłać je do klienta w formacie JSON, a także dodawać nowe dane, aktualizować i usuwać je. Wszystko to zgodnie z dobrymi praktykami pracy z REST.
Chwilę później czeka Cię kolejny praktyczny projekt - skracacz linków. Stworzymy wspólnie aplikację, która pozwoli skrócić dowolny adres URL do krótkiej formy. Oczywiście będzie działać również w drugą stronę! Kiedy przejdziemy pod skrócony adres, przekieruje nas do odpowiedniej witryny. Node.js to platforma wręcz stworzona do tworzenia aplikacji typu real-time, dlatego w kolejnym rodziale podejmiemy temat technologii WebSockets. Dowiesz się jakie są jej zalety, a także jak pracować z nią po stronie serwera i klienta. Tutaj czeka nas kolejny praktyczny, niezwykle ciekawy projekt. Wykonamy bowiem czat grupowy. Aplikacja ta pozwoli dowolnej ilości użytkowników podłączyć się do czatu podając swój nick, a następnie wysyłać widoczne dla wszystkich wiadomości. Zaimplementujemy nawet takie rozwiązania jak wyświetlanie statusów o dołączeniu kogoś do czatu, a także o jego opuszczeniu.W przedostatnim rodziale tego kursu podejmiemy tematykę dobrych praktyk pracy z Node.js. Na początku omówimy najważniejsze konstrukcje nowej specyfikacji EcmaScript 2015, które są znakomicie wspierane w Node. Chwilę później omówimy jak korzystanie z Promises uprości, a także ulepszy nasz kod. Wśród dobrych praktyk nie zabraknie również informacji o debugowaniu aplikacji. Zobaczysz sprawdzone sposoby, by znaleźć błędy lub lepiej, krok po kroku, zrozumieć jak działa napisany wcześniej kod. Dowiesz się również jak pracować z błędami, by Twoje aplikacje działały w sposób przewidywalny.
Ostatni rozdział w całości został poświęcony temu, co zwykle jest pomijane w innych materiałach, mianowicie wdrażaniu aplikacji do produkcji. Przez cały ten kurs pracować będziemy lokalnie, lecz kiedy aplikacje są już gotowe, wypadałoby udostępnić je światu. Wdrażanie aplikacji napisanych z użyciem Node.js nie jest jednak tak oczywiste, jak np. wgrywanie WordPress’a u wybranego hostingodawcy. Ty będziesz miał jednak możliwość zobaczyć, jak wdrożyć napisany przez nas grupowy czat na platformie Heroku. Jest to bardzo popularny serwis działający jako PaaS (Platform as a Service). Za darmo będziesz mógł w ciągu kilku chwil uruchomić swoją aplikację.Zobaczysz o co należy zadbać, by wszystko poszło gładko. Serwisy typu PaaS dbają o bardzo wiele aspektów wdrażania i serwowania naszych aplikacji, takich jak bezpieczeństwo oraz nieustanną dostępność. Mają jednak pewne ograniczenia. Z tego powodu, dowiesz się również jak wdrożyć swoją aplikację na serwerze wirtualnym VPS z systemem Ubuntu Server. Takie rozwiązanie daje nam całkowitą kontrolę, ale także obarczone jest większą odpowiedzialnością. Zaczniemy od instalacji na serwerze platformy Node.js, systemu kontroli wersji GIT, a także innych niezbędnych modułów
Następnie zobaczysz jak skonfigurować swoje lokalne środowisko tak, by za pomocą GIT’a wysyłać kod do zdalnego serwera, a potem jednym poleceniem wdrażać go do produkcji. Dowiesz się również jak jednocześnie serwować wersję produkcyjną oraz developmencką. W rodziale tym poruszymy również inne kwestie, takie jak procesy potomne czy tworzenie klastrów. Dzięki tej wiedzy, będziesz mógł maksymalnie wykorzystać dostępne zasoby serwera. Na sam koniec tego kursu rzucimy okiem również na inne, nie przedstawione wcześniej zastosowania Node, a także nakreślimy dalszą drogę nauki w tym zakresie.
Kurs ten jest dla wszystkich osób, które dobrze czują się w technologiach frontendowych, tj. HTML, CSS i JavaScript, a teraz chcą rozpocząć swoją przygodę z back-endem. Im zatem lepiej znasz język JavaScript, tym więcej wyciśniesz z Node, natomiast nie jest wymagana bardzo zaawansowana wiedza z zakresu tego języka.
Mamy jeszcze jedną ciekawą konstrukcję w ECMAScript 2015, o której w poprzedniej lekcji nie powiedziałem.
Mianowicie Promises. I teraz chcę ci pokazać, w jaki sposób Promises działają.
Nie chcę tego specjalnie tłumaczyć na siłę na język polski i mówić "obietnica".
Może lepiej pozostańmy przy tej terminologii angielskiej.
Na początek mamy taki przykład, który doskonale powinieneś rozumieć, bo już z niego korzystaliśmy w tym
kursie. Czyli definiuję sobie funkcję readFile.
Chciałbym tutaj przekazywać plik do otwarcia i callback.
Jeżeli się uda otworzyć taki plik, otwieramy go fs.readFile, czyli modułem standardowym wbudowany w Node,
to wtedy callback wykonamy z null i z data.
Natomiast jeżeli nie, to z errorem.
Dokładnie tak to będzie wyglądać.
Czyli tutaj tę funkcję wykonuję i przekazuję sobie callback,
czyli to wszystko,
przypomnę - wpada pod cb, a więc będzie wywoływane z errorem lub z null i z data.
Nie ma problemu.
Zobaczmy, w jaki sposób to zadziała.
Wywołamy to poprzez node index, no i zobacz, wszystko zostało odczytane i zadziałało.
Natomiast teraz chcę ci pokazać, jak można ten kod zamienić na kod używający Promise
i dlaczego jest to lepsze rozwiązanie.
Będziemy chcieli sobie tę funkcję skopiować i następnie wszystko to wykomentujemy.
Przejdziemy sobie teraz niżej, wkleję tę funkcję i przerobimy ją na taką, która wykorzystuje Promise. Mianowicie na samej
górze zrobimy sobie zmienną p i napiszemy new Promise. Mamy taki obiekt globalnie dostępny,
to nie jest w Node.js, to jest po prostu w ECMAScript 6,
Czyli tutaj silnik V8 ma to zaimplementowane i część przeglądarek również.
I teraz taki Promise przyjmuje dwie funkcje, a w zasadzie jedną funkcję, ale z dwoma parametrami: resolve i reject.
I to będą funkcje, które będzie można wywołać. Teraz wszystko, co mieliśmy tutaj, wstawimy sobie do takiej Promise,
a w następnej lekcji jeszcze pokażę ci, jak to uprościć
ze specjalną biblioteką. I teraz o co chodzi?
Otóż będziemy chcieli wywołać resolve wtedy, kiedy wszystko będzie ok i reject wtedy, kiedy wystąpi jakiś błąd.
Więc co tutaj zrobić?
Nie będziemy tego callbacka w ogóle już przyjmować do funkcji readFile.
Nie będziemy go w ten sposób wywoływać, ale będziemy wywoływać funkcję reject, którą nam tutaj przekaże
ten interpreter jakby JavaScript i będziemy wykonywać drugą funkcję, czyli resolve.
Czyli pamiętaj, że wykonujemy funkcję resolve, jeżeli jest wszystko w porządku. Możemy jej przekazać, co
tylko chcemy.
I podobnie jeżeli chodzi o reject.
No i teraz wydaje się, że ten kod jest taki trochę dłuższy, że może trochę gorzej wygląda, ale zobaczysz,
w jaki sposób będziemy z niego korzystać.
Już nie tak jak tutaj, bo tutaj tak z tego korzystaliśmy, ale zrobimy sobie readFile.
Część z tego kodu nam się przyda, więc to wykomentuję.
Teraz napiszę then i podam
funkcję. Ta funkcja przyjmie data i będę chciał sobie tę datę wyświetlić.
Ok. I teraz mógłbyś zapytać, skąd wzięło się to then?
No właśnie. Then jest dostępne na obiekcie Promise. Natomiast readFile jako funkcja nie zwraca nam nic, czyli zwraca
undefined, a więc tutaj będzie błąd.
Dlatego tutaj stworzyłem zmienną p u góry i na samym końcu musimy ją zwrócić.
Oznacza to tylko tyle, że jeżeli wywołamy funkcję readFile, ona to, co ma tutaj, to poodkłada sobie
na później z tego powodu że readFile jest asynchroniczne, przejdzie sobie w to miejsce, bo pamiętasz, jak
działała nasza jednowątkowa pętla zdarzeń, to oddeleguje, przejdzie tutaj wykonywać dalej kod, zwróci to
p, czyli kiedy ta funkcja się zakończy,
to w tym miejscu będziemy mieli zwrócone jakby to p.
I to właśnie p,
czyli Promise,
zawiera w sobie metodę then.
Ta metoda zostanie wykonana wtedy, kiedy się zakończy wykonywanie takiego Promise, czyli albo reject, albo resolve.
Natomiast pierwszy parametr, pierwsza funkcja przekazana do then, to będzie ta, która jest tutaj, czyli resolve.
Kiedy się wykonuje resolve i przekazujemy tutaj te dane, to dokładnie wykonujemy tę pierwszą funkcję.
Natomiast możemy po przecinku podać drugi parametr, drugą funkcję. I ona się wykona wtedy, kiedy wykona
się reject.
Natomiast jeszcze łatwiej można to zrobić, wpisując tutaj catch. I catch
działa w ten sposób, że wykona się wtedy właśnie, kiedy będzie reject.
Zauważ, że możemy to łańcuchowo łączyć. Czyli metoda then tak naprawdę sama w sobie zwraca dalej tę Promise
i można z niej korzystać.
Czyli pod catch mamy to, co będzie tutaj w reject, czyli error.
W zasadzie to musimy funkcję tutaj wpisać.
Ta funkcja dopiero to przyjmie.
Dlatego zróbmy to samo, co robiliśmy wyżej.
Wystąpił błąd i teraz sobie to przetestujemy.
Zobaczmy, czy efekt będzie taki sam.
Okazuje się, że wszystko jest w porządku. Ale spróbujmy otworzyć plik, który nie istnieje.
lorem6.
No i mamy: Wystąpił błąd.
W ten sposób ten kod jest dużo czystszy, jak możesz zobaczyć, bo korzystamy właśnie z Promise, a ona działa w
ten sposób, że może wykonywać dowolne zadanie asynchronicznie, bo skoro funkcja readFile jest asynchroniczna,
może się wykonać za 10 milisekund, może się wykonać tak naprawdę za 5 sekund.
No ale kiedy ona się wykona, to ona dopiero wtedy wykona reject albo resolve. I dopiero wtedy wykona
się nasza funkcja, która jest tutaj, albo która jest tutaj. To, co jest ciekawe, to z tymi danymi możemy
tutaj robić, co chcemy w kilku funkcjach. Czyli powiedzmy, że tu robiliśmy tylko console.log. Jeszcze to sobie
uproszczę. Ale przypiszmy kolejny then w tym miejscu.
Później przypiszemy catch pod spodem. I w zasadzie nawet możemy to zrobić tak, żeby to było bardziej czytelne.
W JavaScript nie ma to znaczenia. I tutaj przypiszemy kolejny then, który przyjmie data. Ale on je przyjmie
tylko jeżeli je tutaj zwrócimy, czyli return data z tej funkcji i możemy z tym coś znowu zrobić. Czyli zróbmy
sobie np. data.ToUpperCase i dopiero później będziemy mieli catch.
Zobaczmy. Wystąpił błąd, tak, bo podajemy plik, który nie istnieje. Podajmy sobie ten, który istniał, i zauważ,
że mamy tutaj toUpperCase, czyli powiększone, ale wcześniej mieliśmy normalną wersję. W ten właśnie sposób możemy
sobie dowolnie łączyć takie wywołania then, czyli robić coś w jakiejś jednej funkcji z tym, co nam zwróciło resolve,
a później możemy przypisać kolejną funkcję, kolejną itd.
I w jakiś sposób możemy coś robić ze zwróconymi danymi.
Mogą to być dowolne oczywiście dane.
I teraz na koniec pokażę ci jeszcze jedną rzecz, która tutaj jest niesłychanie ciekawa i bardzo ważna.
Otóż wspomniałam ci dopiero co, że możemy w tych funkcjach robić różne rzeczy.
Tutaj moglibyśmy najróżniejsze funkcje podstawić i coś w nich robić.
Natomiast jeżeli w dowolnej z tych funkcji wystąpi jakiś błąd - wyobraźmy sobie, że coś tutaj robiliśmy
z tymi danymi i został wyrzucony w błąd albo to my go wyrzucimy.
Ja tutaj wyrzucę taki błąd ręcznie można powiedzieć, czyli throw new Error.
I tutaj sobie napiszę: własny błąd.
Data oczywiście w tym momencie już nie zadziała, ale nie ma to znaczenia.
Zobaczmy. Jak widzisz, to co się miało wyświetlić, to się wyświetliło, bo było to tutaj w tym console.logu.
Więc żeby on się nie wyświetlił, to jeszcze to wstawmy o jeden krok wyżej.
Czyli coś robimy i wystąpił błąd. To zauważ, że od razu pojawiliśmy się w bloku catch.
To jest niesłychanie ciekawe, że ten błąd został tutaj przechwycony, chociaż go wyrzuciłem w tym miejscu
i został obsłużony poprawnie, czyli error, message z tego błędu zostało wzięte.
Ale jeżeli byliśmy np. tutaj i zrobiliśmy reject z błędem, bo taki plik nie istniał, to to również zostało
obsłużone tutaj w catch.
Czyli to jest fajne, że możemy w sobie w takim Promise łączyć wywołania takich funkcji, a na końcu zrobić catch
i to catch wywoła się, jeżeli w dowolnym miejscu gdziekolwiek wystąpi jakiś błąd. To by było tyle tytułem
wstępu do Promise.
Możesz z nich korzystać w taki natywny sposób tutaj bezpośrednio, ale w kolejnej lekcji pokażę ci, że lepiej
skorzystać z biblioteki Q, która dodaje nam jeszcze wiele ciekawych funkcji i w trochę ładniejszy sposób
pozwala nam to wszystko formatować.