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, jak możemy radzić sobie z serwowaniem statycznych plików.
Już poczyniłem pewne kroki, zaraz ci je wytłumaczę. Natomiast zobacz, co mamy tutaj, w katalogu public.
Otóż mamy tutaj plik index.html, który w sobie zawiera odwołanie do images/cat.jpg, czyli do obrazka, który jest
tutaj w images, a także u góry do stylów, czyli taka standardowa strona internetowa. Natomiast do tej pory
zobaczyłeś już w lekcji poprzedniej, że nie jest to takie proste, że jeżeli tutaj wpiszemy css/style.css, to serwer
nam to odeśle.
Tak byłoby, gdybyśmy korzystali z serwera typu Apache, który na pewno znasz.
Natomiast w Node.js po prostu przeglądarka wyśle zapytanie pod css/style.css i my to będziemy
mieli pod request.url.
I to wszystko.
To nie oznacza, że takie style zostaną odesłane.
Dlatego chcę ci teraz pokazać, jak można sobie z tym poradzić i zrobimy to za pomocą streamu.
Przejdźmy tutaj. Tworzę serwer, tak jak to widziałeś do tej pory.
Mamy request oraz response.
Ta funkcja się wykona, przypominam, dla każdego nowego połączenia. My go wykonamy z przeglądarki internetowej.
Tworzę dwie zmienne fileName i file na wartość null.
I teraz będę chciał sprawdzić, czyli req.url
jest to slash albo index.html.
Innymi słowy czy przeszliśmy na ten serwer pod taki adres, czy np. dopisaliśmy index.html.
Teraz ten serwer nie działa, ale o to tutaj chodzi.
Jeżeli tak, to chcę ustawić zmienną fileName na index.html, do else wrócimy za moment.
No to teraz skoro wiemy, że ktoś chce pobrać plik index.html, to co możemy zrobić?
Otóż chciałbym ten plik odczytać, a on znajduje się w katalogu public - tutaj - i jego treść przesłać dalej.
Mówiłem ci, że zarówno request, jak i response są to streamy.
Dlatego będę chciał teraz utworzyć sobie za pomocą modułu file system nowy odczytywalny stream z tego
pliku index.
Zrobimy to w tym miejscu. Napiszę file. Nie tworzę tutaj var file, bo wcześniej to zrobiłem tutaj i teraz
fs.createReadStream.
Doskonale to już znasz. Metodą path.join
będziemy chcieli połączyć dirname, czyli tu gdzie jesteśmy, z public i następnie z fileName, czyli na razie
będzie to index.html. I skoro mamy taki readstream, to możemy teraz zacząć go odczytywać i przesyłać dalej.
Mówiłem ci, że response jest to nic innego jak stream, więc możemy napisać file.pipe(response).
W ten sposób zaczynamy odczytywać ten plik w różnych ilościach i następnie w takich chunkach przesyłamy
go dalej do przeglądarki.
W ten sposób przeglądarka potrafi sobie z tym radzić.
Nigdy nie otrzymuje całej strony internetowej na raz, tylko właśnie po kawałku.
Niemniej jednak dobrze byłoby jeszcze dać znać przeglądarce, jaki jest to mime type.
Dlatego wpiszemy sobie to w tym miejscu.
Będzie to status 200, czyli wszystko w porządku, i następnie content type równa się text/html.
Zobaczmy, jak to będzie działać. Uruchamiam serwer za pomocą node index. Serwer działa. Spróbujmy tutaj odświeżyć
i mamy jakiś błąd.
Path must be a string. Received null.
Przy okazji jedna bardzo ważna uwaga. Serwer nam padnie, jeżeli wystąpi na nim jakiś błąd.
Jest to bardzo istotne i w kontekście Node.js jeszcze o tym będziemy mówić.
Ok.
Czyli gdzieś popełniliśmy błąd. W zasadzie to tutaj powinno zostać pokazane, w którym miejscu to się nam
zdarzyło. Czyli w linijce 16 - widać to tutaj.
Wygląda na to, że przekazana zmienna fileName ma wartość null.
Czyli tak naprawdę to, co jest tutaj, nigdy się nie wykonało.
Dlaczego tak się stało?
Może zróbmy sobie na początku console.log(req.url) i zobaczmy, dlaczego taki błąd mógł wystąpić.
Raz jeszcze serwer uruchomię.
Zauważ, że on działa do momentu, aż nie wykona się ta linijka. Ona się wykona dopiero, kiedy ktoś się
podłączy.
Więc zobaczmy.
No i widzimy, że jest to /index.html. Czyli to ja popełniłem błąd, wpisując to tutaj, bo tu powinniśmy
z ukośnikiem to wpisać.
I teraz nie powinno być problemu. Console.log usuniemy. Raz jeszcze wywołam serwer. Odświeżę i teraz wszystko
działa jak należy.
Niemniej jednak przeglądarka otrzymała kod HTML, można go podejrzeć wciskając Control lub Command+U.
Tutaj w zasadzie na Macu będzie to Alt+Command+U, na Windowsie Control+U, no i widzimy, że taki kod otrzymała.
Ale zauważ, co stało się tutaj. Przeglądarka parsując kod HTML, zobaczyła odwołanie do stylów, więc wysłała
sobie zapytanie do serwera po te style. Zobaczmy, co się wtedy stało.
Przeszliśmy w to miejsce.
Nie pasowało to zapytanie ani do slasha, ani do index, więc byliśmy tutaj. Nic się tutaj nie stało, więc fileName
ma wartość null.
W tym miejscu zostało podstawione null i dlatego serwer nam się wyłożył.
Więc widzisz, że przeglądarka wysłała więcej zapytań niż my, wciskając tutaj Enter.
Dlatego teraz będę chciał pozostały kod również uzupełnić w taki sposób. Skoro wiemy, że req.url na
początku ma slasha, tak jak tutaj - właśnie index.html.
Jeżeli wyślemy go po style, tak jak to zrobiła przeglądarka, również na początku będzie slash. Dlatego
chcę usunąć tego slasha i za pomocą slice na takim stringu usunę sobie wszystko czy w zasadzie wytnę wszystko od pierwszego znaku w górę.
I wtedy będziemy tutaj mieli nazwę takiego pliku, np. css/style.css. Skoro tutaj będziemy mieli właśnie
taki plik.
No to za pomocą createReadStream będziemy mogli do niego utworzyć właśnie taki stream odczytywalny i będziemy
ten plik mogli wysłać dalej.
Natomiast co wtedy, gdy plik nie będzie istniał? Tutaj przeglądarka wyśle zapytanie po style, które istnieją
czy po zdjęcie kota, ale jeżeli plik nie istnieje, to pojawi się błąd.
Dlatego do tego file, czyli do streamu, chcę przypisać zdarzenie on error, czyli na błąd.
Będzie to funkcja, która przyjmie ten błąd.
On nam akurat nie będzie potrzebny, ale jedna rzecz, którą będziemy chcieli zrobić, to res.writeHead
i napiszemy sobie 404. I jako drugi parametr możemy tutaj podać Not Found,
czyli będzie to wiadomość. I następnie chcę zakończyć res.end. Czyli kiedy przejdziemy tutaj niżej,
to ani writeHead, ani to pipe już nie będzie działać, nie będziemy mieli żadnego błędu,
ale to nie będzie działać dlatego, że wcześniej skorzystaliśmy z res.end.
Mam nadzieję, że rozumiesz.
Czyli jeżeli plik nie będzie istniał w katalogu public, to będziemy mieli błąd i wyświetlimy 404. Jeżeli
będzie istniał, to powinien zostać zaserwowany.
Ale pojawi się taki problem, że w przypadku stylów CSS zaserwujemy je z takim content type text/html,
więc i o to musimy sobie zadbać.
Dlatego na samej górze tutaj poza w ogóle funkcją, która jest dla każdego połączenia tworzona, stworzymy
sobie coś takiego. Stworzyłem po prostu tablicę, którą nazwałem MIME_TYPES i rozszerzenia, a także ich
typy mime.
Te, które będą nam tutaj potrzebne. I teraz w tym miejscu zamiast na sztywno wpisywać html, to odwołamy się
do MIME_TYPES,
które mieliśmy u góry. I tutaj musimy podać na przykład .html. Skąd wziąć takie rozszerzenie?
Otóż mamy ten plik fileName tutaj, więc będziemy chcieli odwołać się do path.extname - również już widziałeś
tę metodę. Ona nam ze ścieżki zwraca rozszerzenie.
I tutaj podamy fileName, czyli zwróci nam np. rozszerzenie .css albo .html i mime types
odpowiedni nam również zwróci.
Teraz to powinno zadziałać. Serwer nam się zatrzymał, bo był błąd, więc wystarczy go uruchomić od nowa.
Odświeżymy stronę. I teraz i style zostały poprawnie odesłane tutaj, i obrazek. I nie mamy żadnego błędu.
Jeżeli tutaj przejdę do Inspect i do Network, odświeżymy, to zobaczymy, że te kilka rzeczy zostały przesłane
i np. jeżeli kliknę na style, to response headers, content type, to poprawnie text/CSS, a w przypadku kota
mamy image/jpg.
Jak widzisz, to nie wzięło się z sufitu.
Musieliśmy to zaprogramować tutaj na samej górze.
Jeżeli natomiast wyślemy zapytanie po plik, który nie istnieje, niech to będzie na przykład jakiś inny
obrazek - powiedzmy cat2.jpg, to będziemy tutaj mieli 404, ale ponownie serwer się nam nie wyłoży, bo poprawnie
to obsłużyliśmy.
W ten sposób możemy serwować pliki statyczne. Jak widzisz, znowu troszkę tutaj musieliśmy pomyśleć i nie
mamy wszystkich typów mime itd.
Ten kod nie jest doskonały, dlatego znowu widzisz, że owszem, możemy na niskim poziomie to wszystko zaprogramować,
ale jest to żmudne i wymaga sporej wiedzy, aby zrobić to dobrze.
Jest to kolejny powód, dla którego już niebawem będziemy korzystać z modułów napisanych przez ludzi, którzy
wiedzą o tym trochę więcej niż my.