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 rozpoczniemy wykonywanie już trochę bardziej zaawansowanego, praktycznego projektu, a będzie
to taka aplikacja CLI, czyli Command Line Interface, która pozwoli zmieniać grupowo nazwy plików. W folderze
siedemnastym, w którym teraz jesteśmy,
mamy katalog images i w środku kilka różnych plików png. Specjalnie nie umieszczałem tutaj jakichś zdjęć,
aby w plikach źródłowych one dużo nie zajmowały.
Natomiast jakby idea tego programiku jest taka, aby on np. dla zdjęć zmieniał nazwy grupowo. Zaraz zobaczysz jak.
Więc mam tutaj trochę takich obrazków malutkich 50 x 50 pikseli. I będziemy chcieli zmienić grupowo
ich nazwy, tak aby zamienić je np. na photo-001, photo-002, photo-003 i tak dalej.
Generalnie możesz zobaczyć tutaj, że już poczyniłem pewne kroki i komentarze.
Będę chciał, aby nasz plik index.js wywoływać w taki sposób, czyli standardowo: node index.js lub po prostu
bez rozszerzenia, a następnie abyśmy mogli przekazać parametry, które pliki chcemy zmieniać. Czyli chcę
sobie podać taki parametr jak dir i następnie files.
To oznacza, że chcę zmienić nazwy wszystkich plików, które są w katalogu files.
Następnie chcę przefiltrować, aby zmieniać nazwy wyłącznie tych plików, które mają podane rozszerzenie png
i format, o którym wspominałem, będzie tutaj. Czyli chcę np. nazwę photo + pauza
i tutaj trzy $.
No i to jest właśnie fajne, że będziemy mogli sobie podać takie trzy $ i wtedy to będzie zamieniane
np. na 001,
Jeżeli podamy dwa, to na 01, jeżeli podamy jeden, no to będziemy mieli 1, 2, 3, 4 itd.
Za moment zobaczysz. To, co tutaj widzisz,
to nie są jakieś cuda.
Tak po prostu sobie wymyśliłem, że ta aplikacja ma być wywoływana i będziemy musieli napisać ją w ten
sposób, aby rzeczywiście po wykonaniu takiej linijki w folderze siedemnastym to wszystko zadziałało.
Poniżej zrobiłem komentarze i w tej lekcji będziemy chcieli zrealizować dwa pierwsze, a więc sprawdzić
poprawność podanych argumentów, bo wszystkie z nich będą musiały być podane i odczytać pliki z katalogu.
Natomiast później będziemy chcieli przefiltrować jeszcze te pliki, aby tylko png były zwracane, następnie
posortować te pliki według daty i zmienić na końcu nazwę każdego z tych plików. Dlatego będziemy chcieli
zacząć od kroku pierwszego, a więc sprawdzenia poprawności argumentów. W tym kursie, w lekcji o procesie
pokazałem ci, że możemy odwołać się i zrobimy to w console.log do process.argv
i zobaczymy tam tablicę ze wszystkimi podanymi argumentami.
Dlatego przejdźmy sobie teraz tutaj i napiszę node index.
Dwa pierwsze parametry czy dwa pierwsze argumenty zawsze będą takie, czyli ścieżka do pliku wykonywalnego Node
i do aktualnego katalogu. Pokazywałem ci, że aby się ich pozbyć z tej tablicy, możemy napisać slice(2) i
obetniemy sobie te dwa argumenty - będzie pusta tablica. Natomiast teraz pokażę ci, jak to będzie wyglądać,
kiedy wywołamy sobie wszystko z takimi oto parametrami. Pamiętaj, że coś takiego jak dwie kreski nie jest
konieczne.
Nie musimy tego wcale podawać, ale najczęściej w programach tego typu tak podajemy parametry, więc również
tak sobie to zapisałem.
Teraz jeżeli sobie to wyświetlimy, znowu mając tutaj to slice(2), czyli dwa pierwsze wyrzucamy, to tak wygląda nasza
tablica z argumentami, czyli dir, files itd.
Po kolei one wszystkie są podane. I teraz będę chciał w osobnym module, abyśmy sobie stworzyli taką funkcję
pomocniczą, która pozwoli nam pobrać odpowiednią wartość, czyli będę chciał jej np. powiedzieć: "Wyświetl
mi parametry dir", a ona wyświetli mi files. Jeżeli powiem "ext", to wyświetli mi png.
Będzie to dosyć proste, natomiast chcę sobie to utworzyć w zewnętrznym module, który tutaj będziemy importować.
Dlatego najpierw go zaimportuję - w ten sposób. Nazwiemy go argv i zrobimy require.
Pamiętasz, że moduły, które są u nas w katalogu, w ten sposób sobie inkludujemy i nazwiemy również argv.
Skoro go importuję, to musimy go utworzyć. W katalogu 17
utwórzmy sobie taki oto plik. Czyli będzie to argv.js i w nim będziemy chcieli stworzyć całą logikę. Na końcu będzie
musiał być module.exports -
myślę, że to pamiętasz - abyśmy mogli tutaj importować. Więc to, co mieliśmy w tym miejscu, ja sobie wytnę. Wstawię
tutaj.
Ale zamiast console.log będę chciał to zapisać w stałej, którą nazwiemy argv, może w ten sposób. Albo może
nazwijmy to args jak argumenty, funkcję zaraz nazwiemy argv.
No i teraz chcę sobie stworzyć funkcję, którą nazwę argv i ona będzie przyjmować nazwę takiego argumentu.
Czyli np. chcę jej przekazać dir, a żeby ona zwróciła mi files, czyli tutaj sobie możemy nazwać to arg.
I teraz nie będzie tutaj nic skomplikowanego, ale będę chciał ci to wytłumaczyć. Stworzę zmienną index.
W ten sposób. I zauważ, co tutaj robię.
Otóż odwołuję się do argumentów, które mamy tutaj. Jest to tablica, którą dokładnie widzisz w tym miejscu.
I następnie w JavaScript mamy na tablicach metodę indexOf i możemy podać jakiś element. Czyli będę chciał
do tej funkcji argv przekazać np. dir -
bez tych kresek - żebyśmy nie musieli tego podawać. Więc chcę takie kreski dodać do tego, czyli powiedzmy,
że tutaj było dir, to będziemy sprawdzać pod jakim indeksem w tej tablicy znajduje się taki dir. Jeżeli będzie znaleziony,
w tym przypadku jest to zero,
to będzie wszystko ok. Jeżeli nie zostanie znaleziony, to zostanie zwrócone -1. Czyli jeżeli zostanie
znaleziony,
poniżej możemy sobie to sprawdzić za pomocą if, czyli jeżeli index nie jest równy -1, to znaczy,
że to, co podałem, znajduje się w takiej tablicy, ale dodatkowo chcę sprawdzić, czy następny element też
istnieje.
Bo zauważ, że powiedzmy przy wywołaniu tutaj podalibyśmy format, ale nie podalibyśmy photo - w ten sposób -
no to rzeczywiście zostanie znaleziony taki element jak format, kiedy go tutaj przekażę,
ale nie ma już następnego elementu takiego jak photo.
Dlatego tutaj sobie sprawdzimy po pierwsze czy akurat - wróćmy sobie do przykładu z dir - czy dir
istnieje w tej tablicy, a następnie czy element obok niego również jest w tej tablicy.
To łatwo zrobić. Tutaj sobie to połączymy poprzez & i odwołamy się do args.
Następnie do tego indeksu, czyli w naszym przypadku dir będzie pod zerowym indeksem, dodamy sobie 1. Będziemy
tutaj, czyli index + 1. W tej tablicy się odwołamy, sprawdzimy, czy coś takiego istnieje.
Możemy się odwołać i porównać, czy nie ma to wartości undefined, ale nie musielibyśmy, wystarczy,
czy taki element tam jest.
Jeżeli tak, no to mamy taki element i możemy go po prostu zwrócić z tej funkcji poprzez return args
[index + 1].
Mam nadzieję, że to jest na razie proste.
Czyli jeżeli podałem sobie dir, to sprawdzamy, czy dir jest. Jeżeli jest
i następny element też jest, to ten następny element dokładnie zwracamy. W przeciwnym wypadku będę chciał
zwrócić null. I już mamy taką funkcję, więc teraz ją wyeksportujemy.
Odwołamy się do module.exports i będę chciał tutaj eksportować obiekt. W tym obiekcie chciałbym, żeby była funkcja get.
I ona będzie kierować na argv, czyli na tę właśnie funkcję.
Jeżeli coś takiego zrobimy, to po tej stronie po zaimportowaniu tego wszystkiego będziemy mieli ten obiekt,
czyli będzie w nim metoda get.
Mam nadzieję, że to jest jasne, więc spróbujmy sobie w console.log odwołać się do argv, które mamy zaimportowane
u góry i następnie do metody get. I podamy na przykład dir bez tych dwóch kresek, bo je tam dodajemy.
Zobaczymy, czy nam to zadziała. Więc wywołajmy sobie tutaj raz jeszcze.
No i widzimy files, czyli rzeczywiście to zadziałało.
Jeżeli tutaj zmienię np. na ext, to też zadziała, ale zmieńmy na format - powinno zostać zwrócone null.
Zauważ, że mamy null,
bo właśnie po format już nic nie podaliśmy, a powinno być tutaj podane jeszcze to photo.
Niemniej jednak nasza funkcja już działa. Aby nasza aplikacja mogła działać poprawnie, wszystkie te parametry,
które są w tym miejscu, muszą zostać podane.
Jeżeli nie, to chciałbym wyświetlić błąd.
Więc jak możemy to zrobić?
Otóż na początku w tym pliku napisałam "Sprawdz poprawność podanych argumentów", więc chciałbym tutaj
mieć możliwość sprawdzenia tego, czy wszystkie argumenty zostały podane i chciałbym, żeby to wyglądało
następująco. Chciałbym, aby obiekt argv, który zaimportowałem, miał metodę validate - za moment ją otworzymy -
ja jej przekazuję parametry, które muszą tam być, czyli dir, ext i format. Zauważ, że to są te dokładnie, które
tutaj podawałem i chciałbym, żeby ta metoda zwróciła false lub true w zależności od tego, czy wszystkie z tych
parametrów zostały podane.
Jeżeli zwróci false, to chcę tutaj zakończyć naszą aplikację i zwrócić błąd, bo nie zostały podane wszystkie
parametry, a więc nie uda się zmienić nazw plików, co będziemy robić później.
Dlatego wróćmy teraz tutaj do argv.js i utworzymy sobie metodę validate. Zrobimy sobie to w tym miejscu. Funkcję
znowu można nazwać dowolnie, a na dole sobie ją wyeksportujemy,
czyli validate - w ten sposób - i napiszę validate args.
Więc tak też nazwiemy sobie funkcję.
Ona będzie przyjmować parametr dokładnie ten, który tutaj przekazaliśmy, czyli taką oto tablicę i na tej
tablicy sobie tutaj będziemy chcieli iterować. Ja wkleję gotowy kod.
Zauważ, że znowu nie ma tutaj nic skomplikowanego. Utworzyłem zmienną valid, która ma wartość true
i na końcu ją zwracamy. Więc jeżeli wszystko będzie ok, zwrócimy true.
Jeżeli nie, to będziemy zwracać false.
I teraz co robimy?
Otóż po tych argumentach, które zostały przekazane, iterujemy i pod arg będziemy mieli jeden taki
argument, czyli kolejno dir, ext i format. I teraz sobie sprawdzam za pomocą funkcji, którą mamy już u góry,
czyli właśnie tej funkcji,
czy taki argument kolejny się tam znajduje.
Jeżeli nie, czyli not, więc jeżeli zostanie zwrócone null, bo np. nie podaliśmy powiedzmy format, to ustawiamy
valid na wartość false.
No i na końcu skoro valid zwracamy, a zostało przestawione na false, to również false zostanie zwrócone.
Mam nadzieję, że znowu jest to proste.
Nie ma tutaj nic skomplikowanego. I zobaczmy sobie.
Nie mamy żadnego błędu, więc wszystkie parametry zostały podane. Ale jeżeli byśmy sobie nie podali formatu,
w ten oto sposób, no to będziemy mieli błąd.
Nie podano poprawnych parametrów. Czyli do tej pory udało nam się stworzyć zewnętrzny moduł, który po pierwsze
pozwala nam pobierać w bardzo łatwy sposób
wybrane argumenty,
po drugie pozwala nam walidować,
czy wszystkie z nich zostały podane w taki oto prosty sposób.
Drugi krok, który chciałbym teraz zrobić, jest to odczytanie plików z katalogu i na tym tę lekcję zakończymy.
Dlatego zrobimy to teraz tutaj i znowu - metodę już znasz.
Nazywa się ona readDir, ale najpierw będziemy potrzebowali ścieżki. Dlatego utwórzmy sobie zmienną. Nazwałem
ją dir i znaną tobie już metodą path.join
będziemy chcieli połączyć dirname, czyli katalog w którym jesteśmy, i katalog, który został przekazany.
Więc tutaj jeżeli byśmy się odwołali do tego, to mówimy, że chcemy odczytać wszystkie pliki z katalogu
files.
Wiemy, że mamy już metodę do odczytywania parametrów, czyli chcemy dir sobie pobrać za pomocą tej metody -
w tym miejscu i połączyć sobie to wszystko, a więc pod dir - w tym miejscu - będziemy mieli już całą ścieżkę. I ten
katalog teraz będziemy mogli odczytać za pomocą fs.readDir. Wpiszemy dir i następnie funkcja callback
error-first,
czyli jeżeli będzie błąd, to będzie on tutaj.
A jeżeli będzie wszystko ok, to nie będzie błędu, będzie null i pod files będzie tablica z wszystkimi
plikami, które znajdują się w tym katalogu images.
Jak widzisz, jest tutaj kilka plików, ale także kolejny katalog images.
Nie przez przypadek go tutaj umieściłem.
Będziemy go chcieli odfiltrować.
Jeżeli pojawi się błąd, czyli if error, to będę chciał go wyrzucić.
To również zatrzyma naszą aplikację i zobaczymy to w konsoli.
Nie uda się tych plików zmienić.
Natomiast jeżeli nie będzie błędu, to chciałbym przefiltrować wszystkie te pliki z podanym rozszerzeniem,
czyli w tym przypadku png. Czyli nie będę chciał np. operować na katalogu images albo na innych plikach,
które mogą być tutaj z innym rozszerzeniem. Ale tym zajmiemy się już w lekcji kolejnej.
Wstawmy sobie tylko tutaj komentarz, abyśmy wiedzieli, czym będziemy się zajmować.
Na tym przykładzie będziemy kontynuować już za moment.