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.
W tej lekcji pokażę ci, w jaki sposób możesz odczytywać treść plików. Wiesz już, jak odczytywać o nich informacje,
a także jak listować pliki z katalogu.
Teraz pokażę ci, jak możemy dobrać się do ich zawartości. W katalogu 12
mamy znowu katalog files i dokładnie trzy takie pliki, jak mieliśmy wcześniej.
Są to pliki tekstowe. I teraz pokażę ci dwie metody, które pozwolą odczytywać ich zawartość.
Pierwsza z tych metod nazywa się readFile i na file system będziemy z niej korzystać.
Napiszę fs.readFile.
I tutaj znowu musimy podać ścieżkę do takiego pliku, abym nie musiał za każdym razem, tak jak w lekcji poprzedniej,
pisać path.join itd -
to stworzyłem sobie funkcję pomocniczą, której mogę tutaj przekazać nazwę pliku, i zostanie to połączone
z katalogiem files.
Dlatego tutaj skorzystam z file, czyli z tej funkcji, i przekażę nazwę pliku. Będzie to lorem1.txt.
I następnie mamy funkcję, która znowu nazywa się error-first callback.
To znaczy, że jeżeli będzie błąd, będzie on pierwszym parametrem.
Jeżeli nie, to będzie tutaj null, a jako drugi parametr będziemy mieli odczytane z pliku dane. I teraz
będziemy chcieli te dane sobie wyświetlić, jeżeli nie było błędów, w ten sposób.
Jeżeli wystąpi błąd, to dla tego przykładu będziemy robić throw error - na serwerze
nie jest to dobre rozwiązanie, bo zakończy proces. I teraz console.log(data) sobie wyświetlimy.
Zobaczmy, co pojawi się w konsoli - node index.
No i możesz zobaczyć, że pod data znalazł się buffer. O bufferach już mówiliśmy w tym kursie, one również w przypadku
streamów się pojawiały.
Czyli jest to buffer, a więc jeżeli chcę wyświetlić jego zawartość, to musiałbym tutaj napisać toString.
Domyślnie będzie to u UTF-8, ale mogę tutaj podać również inne kodowanie.
Natomiast jeżeli z góry wiesz, że odczytywany plik to będzie plik tekstowy,
bo mógłbyś oczywiście odczytywać inne pliki, np. plik mp3 lub mp4, gdybyś z tymi danymi coś chciał
zrobić, w jakiś inny sposób je analizować,
natomiast jeżeli są to pliki tekstowe, to możemy podać jako drugi parametr tutaj, w tym miejscu, kodowanie,
a jako trzeci dopiero funkcję. UTF-8 jeżeli tutaj wpiszemy, to zobaczysz, jaka będzie różnica.
Wywołajmy to sobie. I jak widzisz, cały plik został tutaj od razu wyświetlony nie jako buffer, ale jako string.
Czyli pod zmienną data
tym razem znalazł się po prostu string. W lekcji poprzedniej wspomniałem ci, że większość z metod do pracy
z plikami ma swoje odpowiedniki synchroniczne, a to oznacza, że jeżeli mamy metodę readFile, to będziemy
mieli również metodę readFileSync.
Dlatego teraz pokażę ci, jak możemy z niej skorzystać poniżej.
W ten sposób. A więc od razu do zmiennej lorem2 chcę sobie odczytać zawartość pliku lorem2.txt i wyświetlić
ją poniżej. To będzie buffer domyślnie, bo tutaj nie przekazałem żadnych innych informacji, dlatego skorzystamy
z metody toString.
Natomiast pokazałem ci w lekcji poprzedniej, że musimy coś takiego otoczyć blokiem try catch.
Tutaj tego nie robię, ale gdyby taki plik nie istniał, to cały proces się wyłoży, bo z tej metody zostanie
wyrzucony poprzez throw
błąd, a nigdzie go nie obsługujemy, nigdzie go nie łapiemy. Dlatego zawsze trzeba to zrobić, ale tutaj dla
przykładu, dla czystości tego przykładu tego nie robię. Może sobie wykomentujmy to, co jest wyżej najpierw.
OK.
I zrobimy node index i znowu zawartość całego pliku nam się wczytała.
Tym razem synchroniczne, a to oznacza, że żaden kod, który był poniżej tej linijki, nie mógł być wykonany,
dopóki nie została przez specjalne API napisane w C++ odczytana zawartość tego pliku.
Takie rozwiązanie czasami również jest pożądane, kiedy np. odczytujemy jakiś plik konfiguracyjny, który jest niezbędny
do tego, aby uruchomić naszą aplikację.
Komentuję to sobie i pokażę ci teraz jeszcze jedną metodę, którą w zasadzie już znasz.
Dlatego tylko ją tutaj wkleję.
Jest to metoda createReadStream, z której możemy odczytywać.
Korzystaliśmy z tego już przy okazji streamów.
Wkleję zatem gotowy kod, a zatem: fs.createReadStream.
Chcę, żeby to był plik lorem3.txt i zauważ jeszcze jedną ciekawą rzecz. Otóż jako drugi parametr przekazuje
obiekt konfiguracyjny, w którym mamy encoding: "utf8". I następnie to wszystko chcę puścić do kolejnego streamu
poprzez pipe, które również już znasz. I tym streamem jest process.stdout, czyli to wszystko będzie po prostu tutaj
wyświetlone. Zobaczmy, jak to wygląda. I w ten sposób wszystko się wyświetliło.
Natomiast to, co jest super ważne, jeżeli podajemy takie kodowanie jak UTF-8 tutaj, to stream zadba o
to, aby odczytywać dane w odpowiednich porcjach. Ja pokazałem ci w lekcji o streamach, że tutaj będą takie
chunki, czyli pojedyncze kawałki odczytywane. I to, co jest ważne, to jeżeli plik jest zapisany w sposób binarny,
nie wiemy, czy tam jest tekst, czy jakieś inne informacje, to jeżeli będziemy odczytywać dane, np. po
32 kb, to może się okazać, że taki odczytany kawałek nie jest w stanie być poprawnie sparsowany na UTF-8.
Mam nadzieję, że wiesz o czym mówię. W lekcjach o streamach
zauważ, że wyświetlaliśmy je jako UTF-8, ale nie podawałem tego kodowania.
Czyli jeżeli w funkcji zwrotnej mielibyśmy taki chunk.
Mam nadzieję, że to pamiętasz. I wpisałem toString, a wcześniej nie powiedzielibyśmy, że ma to być kodowanie
UTF-8, to mogłaby zostać ta zawartość w tym chunku odczytana po prostu jako całe, powiedzmy, 32 kb i metoda toString
nie mogłaby tego poprawnie zamienić na UTF-8, bo brakowałoby tam przykład dwóch bajtów.
Chodzi o to, że jeżeli podamy UTF-8, to taki stream kiedy jest odczytywany, to Node.js dba o to, aby odczytać
dokładnie tyle informacji, ile jest potrzebne, aby poprawnie można było daną porcję zamienić na UTF-8 i
aby żadnych znaków np. ą, ę nie stracić.
Więc jeżeli będziesz odczytywał jakiś plik, który wiesz, że jest plikiem tekstowym, to podawaj kodowanie i zazwyczaj
będzie to UTF-8, bo jest to najpopularniejsze kodowanie i ten plik, który mamy tutaj, w tym przypadku
lorem3 zapisywałem również jako UTF-8.
Podsumowując te metody, to jeżeli korzystamy z readFile, to ta funkcja callback wykona się dopiero wtedy,
kiedy cały ten plik zostanie wczytany do pamięci jako buffer lub tutaj jako string i pod data będzie
dostępny.
Jeżeli jest to duży plik, to zapchamy sobie pamięć operacyjną.
Natomiast aby zadbać o to, by do pamięci operacyjnej nie wczytywać zbyt wielkiej ilości danych, korzystajmy
zawsze ze streamów, bo wiesz, że stream będzie po kawałku wczytywał taki plik. I kiedy my będziemy go konsumować,
np. za pomocą pipe, czyli tutaj wyświetlać, to dopiero wtedy będzie odczytywał kolejne informacje i przekazywał
je dalej. W ten sposób pamięć operacyjna nie będzie zbyt mocno eksploatowana.