Uvod u Node.js
Uvod
Da bismo mogli čitaocima predočiti pravi smisao radnog okruženja kao što je Node.Js, bilo bi potrebno da se u mislima vratimo petnaestak godina unazad, u vreme kada je JavaScript još uvek bio samo front-end jezik, a Node.js nije ni postojao.
Ako bismo, pod navedenim uslovima, proveli neko vreme pišući JS skripte, primetili bismo da je JavaScript prilično elegantan i funkcionalan jezik (pod uslovom da smo u stanju da pravilno odmerimo kako sa njim treba postupati i da se zadržimo u okvirima pravila dobrog programiranja), i spontano bi nam palo na pamet kako bi bilo zanimljivo kada bi lepa i pregledna JS sintaksa mogla da se koristi za pisanje desktop programa i/ili kao jezik za razvoj mrežnih servera ....
Mnogi programeri razmišljali su na navedeni način, a neki su i radili na rešenju.
Pre nešto više od deset godina (2009), mladi američki programer Rajan Dal (Ryan Dahl), kreirao je Node.js - radno okruženje koje koristi program V8 (JavaScript interpretator otvorenog koda iz Google Chrome-a), i povezuje ga sa ulazno-izlaznim mogućnostima operativnog sistema na kome je instaliran, čime opisani sistem poprima odlike navedene u drugom pasusu, tj. (pomalo slikovito), postaje: sistem za pisanje desktop programa preko JavaScript sintakse i takođe (što je programerima posebno "golicalo maštu", dok takva mogućnost nije postojala), okruženje za kreiranje back-end komponenti web aplikacija - preko istog jezika koji se koristi za front-end.
U godinama koje su došle, početna ideja je 'oživela' i radno okruženje Node.js je (zaista) postalo izuzetno popularno, najviše kao serverska tehnologija, ali, takođe i kao osnova svojevrsnog "ekosistema" za kreiranje najraznovrsnijih web aplikacija, desktop programa, biblioteka, modula i drugih softverskih dodataka ....
Pre nego što počnemo (za prave) ....
Kao što smo već pisali, postoje tehnike koje je praktično neophodno poznavati, pre nego što se neko upusti u upoznavanje sa okruženjem Node.js.
Pored samih osnova JavaScript-a i funkcija za rad sa nizovima, potrebno je pre svega biti upoznat sa šablonskim niskama, sa funkcijama povratnog poziva i lambda notacijom, kao i sa ostalim tehnikama o kojima smo već pisali u članku o ES6 sintaksi (koji smo linkovali na početku).
Striktno govoreći, poznavanje PHP-a nije neophodan preduslov za upoznavanje sa Node.js-om, ali, smatraćemo (u nastavku), da ste bar okvirno upoznati i sa PHP-om (pri čemu je, više nego poželjno, da ste sa PHP-om upoznati i 'više nego okvirno' :)), i takođe ćemo smatrati da ste upoznati sa osnovnom internet terminologijom, kao i sa HTTP statusima.
Ukoliko su uslovi koje smo prethodno naveli ispunjeni (bar dobrim delom :)), nastavljamo dalje ....
Što se tiče samog Node.js-a, članak pred vama, kao i drugi članci koje spremamo (koji će za temu imati Node.js i srodne tehnologije), odnosiće se prevashodno (ali ne isključivo), na primenu Node.js-a kao back-end tehnologije i, u takvim okolnostima, sasvim je primereno (i uobičajeno), na početku razmotriti razlike između Node.Js-a i PHP-a (u ovom trenutku, dve najpopularnije tehnologije za razvoj web aplikacija manjeg i srednjeg obima).
Razlike između Node.js-a i PHP-a
Šta je to što Node.js čini veoma zanimljivim radnim okruženjem koje je u pojedinim situacijama bolji izbor od PHP-a i zašto je Node stekao veliku popularnost?
Za razliku od PHP-a, u kome se naredbe izvršavaju sinhrono (što praktično znači da je pokretanje sledeće naredbe "blokirano" sve dok se prethodna naredba u potpunosti ne izvrši), u Node.js-u se naredbe izvršavaju asinhrono, što znači da određene naredbe mogu biti pokrenute pre završetka izvršavanja prethodne naredbe.
U praksi, sve što smo naveli ne znači da se preko Node.js-a 'tek tako' mogu pisati aplikacije u kojima je više procesa pokrenuto istovremeno (Node.js nije 'multi-threaded' okruženje), i ne znači da je "Node.js brži od PHP-a" (pogotovo ne obavezno i uvek), ali, pod određenim okolnostima - može biti brži.
Da pojasnimo, preko tipičnog primera ....
U Node.js-u, pozivi funkcija za pristup bazama podataka, tipično * ne blokiraju dolazeće naredbe (budući da se obrada podataka u bazama obavlja na udaljenom računaru), ali zato pokretanje bilo kakve procesorski zahtevne procedure (recimo, while petlje koja računa decimale broja PI preko iterativne formule i sl) - i te kako može "ukočiti" aplikaciju.
Jednostavno rečeno, u situacijama kada je potrebno brzo obraditi veliki broj zahteva koji nisu previše zahtevni (po pitanju vremena koje je potrebno za pojedinačnu obradu), Node.js može biti vrlo zanimljivo rešenje (koje najčešće pokazuje bolje performanse od PHP-a).
Pored prethodno navedenih objektivnih okolnosti, prilično smo sigurni da mnogi web developeri poslednjih godina daju prednost Node.js-u isključivo iz subjektivnih i/ili estetskih razloga jer, za većinu programera, JS je jednostavno "lepši" i prijemčiviji od PHP-a (to već ostavljamo svakome da samostalno proceni i izabere :)), a u obzir treba uzeti i praktičnu stranu pristupa u kome se jedan jezik koristi: i za frontend, i za backend (u slučaju Node.js-a).
Instalacija okruženja i pokretanje Node aplikacija
U uvodnom članku, želimo pre svega da pokažemo kako se može pokrenuti jednostavan web server preko Node.js-a (pretpostavljamo da je to, ono što čitaoce najviše zanima), ali, prvo ćemo se ipak upoznati sa osnovnim operacijama sa direktorijumima i datotekama, a takođe ćemo se upoznati i sa opcijama za pristup bazama podataka, međutim - pre svega navedenog - potrebno je (naravno), da se upoznamo sa još osnovnijim tehnikalijama ....
Instalacija radnog okruženja
Sa adrese nodejs.org možete preuzeti instalacionu verziju okruženja Node.js koje odgovara operativnom sistemu koji koristite (i potom možete instalirati okruženje Node.js, to jest, Node.js aplikaciju, na vaš računar).
Instalacioni program je mali * i, po završenoj instalaciji, putanja do Node aplikacije biće prijavljena operativnom sistemu.
Kao i u članku koji smo posvetili Python-u, i ovoga puta će komande za pokretanje odgovarati Windows okruženju (u slučaju Linux-a i MacOS-a, putanje prema direktorijumima i samoj izvršnoj datoteci Node.js aplikacije se razlikuju, ali, ostale komande su gotovo istovetne).
Pokretanje korisničkih aplikacija
Pre nego što počnemo da pokrećemo Node aplikacije koje smo samostalno kreirali, potrebno je da prvo utvrdimo 'osnovnu tehničku ispravnost instaliranog okruženja'. :)
Otvorićemo sistemsku konzolu, * i potom ćemo uneti sledeću naredbu (ista naredba važi i za Linux i MacOS okruženja):
Ako je sve u redu, biće prikazan sledeći odgovor (s tim da će verzija u vašem slučaju gotovo sigurno biti drugačija, to jest, novija):
Uspešnim izvršavanjem komande node -v
, ustanovili smo dve stvari:
- pre svega to da je okruženje Node.js uredno instalirano i da je putanja do izvršne datoteke Node.js aplikacije uredno prijavljena operativnom sistemu (što znači da se komanda
node
može pokretati iz bilo kog direktorijuma) - konkretnu verziju paketa Node.js
Sada možemo pokretati i naše sopstvene skripte.
Kao prvo, prostim unošenjem komande node
(bez dodatnih argumenata), dobija se direktan pristup JS interpretatoru:
.... kome se mogu direktno predavati instrukcije.
Ako interpretatoru predamo sledeće komande (jednu za drugom) ....
.... dobićemo sledeće povratne poruke:
Međutim ("generalno"), programeri se vrlo retko obraćaju interpretatoru neposredno (naravno, niko vam ne brani da takav interaktivni pristup (ako vam se baš sviđa), isprobavate do mile volje ).
Umesto pokretanja metodom ručnog zadavanja pojedinačnih komandi, Node apllikacije se gotovo uvek pokreću iz datoteka (s tim da ozbiljnije projekte treba smeštati i u zasebne direktorijume, i pisati za njih konfiguracione JSON datoteke).
Baš kao što smo i do sada radili, kada smo pisali programe i sajtove u drugim jezicima, i ovoga puta kreirajte zaseban direktorijum (tj. 'folder') za vaše Node.js projekte (osnovna adresa koju ćemo mi koristiti u članku je "D:\node_apps"
), i potom sačuvajte, na lokaciji koju ste predvideli za Node aplikacije, datoteku sa nazivom proba.js
(poznata ekstenzija na koju ste već navikli) - i unesite u datoteku sledeći sadržaj:
Otvorite konzolu (ako ste je zatvarali) i unesite sledeće komande:
Na kraju, pokrenite "hello world" aplikaciju;
U konzoli će biti ispisan sledeći tekst:
Sve je (bar naizgled), vrlo slično kao da je pokrenut program koji je pisan u C-u (i skoro isto kao kada smo pokretali Python skripte).
Node Package Manager - npm
Na ovom mestu upoznaćemo se ukratko i sa programom Node Package Manager (u daljem tekstu - npm
), koji olakšava posao instaliranja Node.js modula, biblioteka, ili celokupnih aplikacija.
Neki od modula koje ćemo koristiti, kao što je recimo fs
(File System), deo su osnovne instalacije paketa Node.js (fs
je jedan od tzv. 'core modula'), međutim, drugi moduli se moraju dodatno instalirati (i, u takvim situacijama, "na scenu stupa" npm).
Modul mysql
, koji (očekivano) služi za povezivanje Node aplikacija sa MySql bazama podataka, jedan je od modula koji se moraju dodatno instalirati (a kasnije će nam trebati u članku).
Da bismo instalirali navedeni paket, u konzolu je potrebno uneti sledeću naredbu (podrazumevamo da se još uvek nalazimo u folderu sa Node.js projektima):
Po uspešno završenoj instalaciji, u prilici smo da koristimo modul mysql
(sa svim pripadajućim funkcijama).
U prethodnom slučaju, modul mysql
postao je dostupan aplikaciji koju pišemo, kao i ostalim (manjim) aplikacijama koje možete pokretati iz probnog foldera, ali, aplikacije koje su smeštene u druge foldere - ne mogu koristiti modul mysql
.
Jednostavno, korisnicima je dat izbor da module instaliraju: 'u lokalu' (kao u gornjem primeru), * ili tako da budu dostupni svim Node aplikacijama na računaru, što se postiže preko argumenta -g
('global'):
Menadžer paketa npm
može se koristiti i za inicijalizaciju projekata, ali, u tom slučaju je potrebno da svaki projekat bude smešten u zaseban direktorijum (tj. 'folder').
Pokretanjem sledeće komande (na primer, unutar foldera D:\node_proba\projekat_01
) ....
.... kreira se "prazna" * konfiguraciona JSON datoteka:
Navedeni pristup (inicijalizacija projekta preko programa npm
), svakako pomaže u organizaciji većih i ozbiljnijih projekata, ali, temu inicijalizacije projekata (kao i detaljniji osvrt na modul nodemon
, preko koga se aplikacija automatski restartuje pri svakom čuvanju datoteka iz projekta), ostavićemo za sledeći članak, u kome ćemo detaljno opisati postupak kreiranja web servera uz pomoć paketa Express.js (verovatno najpopularnije biblioteke za Node.js).
Što se tiče osnovne funkcionalnosti Node aplikacija, budući da smo se već bavili osnovnim komandama JavaScript-a kao i drugim temama vezanim za ovaj jezik, nećemo "ponavljati priču" o dobro poznatim C-olikim konstrukcijama JS-a (kao što su grananja, petlje, način definisanja i pozivanja funkcija i sl), ali, iznećemo nekoliko primedbi opšteg tipa, koje se tiču razlika između izvršavanja skripti u browser-u i pokretanja Node.js aplikacija.
Razlike između Node.js-a i izvršavanju JS-a u browseru
Sa jedne strane, Node.js jeste "JavaScript koji se izvršava u konzoli" (što ćemo na primerima jasno videti), ali, sa druge strane - u odnosu na pokretanje JavaScript-a u browseru - postoje određene razlike koje su očigledne i neke razlike koje su 'malo manje očigledne':
- Node.js ima pristup file sistemu, ali, nema pristup: DOM strukturi, browser-u (objektima
window
idocument
) i pripadajućoj funkcionalnosti (kukiji ilocalStorage
) - 'JavaScript u browser-u' nema pristup file sistemu, ali (naravno/očekivano/dobro poznato), ima pristup DOM strukturi i ostalim navedenim opcijama u browserima
U svemu postoji još jedan važan detalj (koji, reklo bi se, ponekad "žulja" programere koji su navikli na standardni JS na web sajtovima) - ne postoji globalni opseg promenljivih, to jest: promenljiva koja je deklarisana izvan funkcija, nije dostupna svim modulima, tj. datotekama unutar projekta (pogotovo ne automatski), već samo onom modulu u kome je deklarisana.
Primer jednostavne Node.js aplikacije
Node.js (kao što smo već naglasili), u ovom trenutku je jedna od najpopularnijih serverskih tehnologija, a priprema HTML sadržaja (kao što takođe znamo), jedan je od osnovnih zadataka u back-end programiranju.
Za sam početak, kreiraćemo vrlo jednostavnu Node.js aplikaciju koja (po pokretanju), čuva HTML dokument na lokaciji po izboru.
Prvo ćemo definisati funkciju koja vraća sadržaj HTML dokumenta (po obrascu sa kojim smo se upoznali u članku o šablonskim niskama):
Za upis u datoteku koristićemo (već najavljenu) funkciju writeFile
iz modula fs
("File System" - jedan od 'core' modula; sadrži funkcije za čitanje i upis u datoteke, i druge funkcije vezane za operativni sistem).
Opšta šema poziva funkcije writeFile
podrazumeva četiri argumenta:
- naziv datoteke (koju je potrebno kreirati i/ili popuniti tekstualnim sadržajem)
- tekstualni sadržaj koji se upisuje u datoteku
- metodu enkodiranja (UTF-8)
- funkciju povratnog poziva, koja će se izvršavati posle (pokušaja) upisa
Četvrti argument je definisan kao funkcija povratnog poziva, koja prima jedan argument (poruku o grešci), a ovoga puta callback funkciju nismo implementirali preko arrow notacije (već kao standardnu imenovanu funkciju), budući da nije u pitanju "funkcija manjeg obima koja se efikasno može zapisati u jednom ili dva reda".
Prethodno navedeno rešenje, u ovom slučaju smatramo elegantnijim i praktičnijim, međutim (budući da nije u pitanju ni funkcija prevelikog obima), takođe se može koristiti i lambda notacija:
Iako smo već najavili da u uvodnom članku nećemo koristiti baze podataka pri pokretanju servera (što će biti tema poslednjeg poglavlja), svakako smatramo da je potrebno upoznati se, već na početku, sa osnovnim funkcijama za pristup bazama podataka preko Node.js-a.
Povezivanje sa MySql bazom i preuzimanje podataka
Da bismo mogli 'išta' da radimo sa MySql bazama (baš kao i u primerima u kojima smo za povezivanje sa bazama koristili PHP), potrebno je prvo obezbediti najosnovniju funkcionalnost, to jest - povezati Node projekat sa bazom podataka.
Kreirajte novu datoteku sa prigodnim nazivom i unesite u datoteku sledeći sadržaj:
Primećujemo da je (jedini) argument funkcije connect
, callback funkcija (koja se aktivira posle povezivanja sa bazom).
Ovoga puta, smatrali smo da je lambda notacija dobar izbor za implementaciju osnovnog povratnog poziva, međutim, konkretnu funkciju preko koje ćemo zapravo čitati sadržaj tabele (iz baze podataka), mnogo radije ćemo implementirati kao standardnu imenovanu funkciju (koju ćemo 'spremiti unapred'):
Funkcija CitanjeTabele
koristi metodu query
(objekta veza
), preko koje se bazi podataka (sa kojom se povezujemo), prosleđuje upit.
Na ovako jednostavnom primeru, ugnežđavanje funkcija i dalje deluje pregledno (bar donekle), međutim, ukoliko nastane situacija u kojoj "povratni pozivi koriste (dodatne) povratne pozive" .... "povratni pozivi povratnih poziva (takođe) koriste povratne pozive" .... (u mislima dodajte još koji 'nivo rekurzije')" .... programski kod više ne bi bio pregledan (ni izdaleka)!
Ugnežđavanje povratnih poziva i (što je mnogo bitnije) - sintaksne konstrukcije koje omogućavaju da se (višestruko) ugnežđeni pozivi izbegnu - biće tema članka o asinhronom programiranju u Javascriptu (koji je već u pripremi i biće objavljen uskoro).
Što se tiče podataka koji su preuzeti iz MySql baze, može se primetiti da je način pristupa podacima idejno sličan načinu pristupa u PHP-u, ali, postoje i razlike:
- umesto da se sadržaj čita red-po-red, preko funkcije (u PHP-u, u pitanju je funkcija
mysqli_fetch_assoc
), redovima se može pristupiti direktno, preko indeksa - za pristup pojedinačnim podacima, koriste se nazivi polja tabele (koji se pojavljuju kao polja objekta
rezultat
).
Za sam kraj uvodne priče o Node.js-u, sledi "ono što smo čekali" - pokrenućemo lokalni web server.
Pokretanje jednostavnog web servera
Kada smo koristili PHP za pokretanje web sajtova, nismo previše brinuli o tome šta radi sam PHP interpretator (što je donekle očekivano, jer, kada se koristi paket kao što je Apache, PHP interpretator se pokreće automatski).
Sa Node aplikacijama, situacija je (sa jedne strane), malo drugačija, i moraćemo sami da pokrenemo server (koji je pre toga potrebno konfigurisati), ali, sa druge strane, procedura nije (iole značajno) komplikovanija u odnosu na proceduru u PHP-u (na koju smo navikli).
U osnovnom smislu, web server je program koji obraća pažnju na mrežni saobraćaj koji je usmeren prema računaru na kome je program pokrenut:
- kada se na odgovarajućem mrežnom portu pojavi zahtev za određenim vidom obrade podataka, server proverava zahtev
- ukoliko je zahtev uredno formatiran - započinje priprema sadržaja koji će biti vraćen korisniku (tj. klijentu), koji je zahtevao sadržaj
Još prostije, korisnici upućuju serveru URL-ove (tj. "putanje"), server tumači putanje, i ukoliko je korisnik naveo jednu od mogućih/dozvoljenih putanja, dobiće od servera odgovor u vidu sadržaja (u browseru će se otvoriti: naslovna stranica, blog, kontakt stranica i sl). *
Ukoliko zahtev nije uredno poslat, odgovor servera će biti neki od "nepovoljnih" HTTP statusa.
Da se vratimo na glavni primer kojim se bavimo ....
Aplikaciju ćemo smestiti u datoteku "probni_server.js"
(za sada nećemo ipak kreirati i zaseban podfolder), unećemo sledeći sadržaj ....
.... i potom ćemo pokrenuti server (kao što bismo pokrenuli bilo koju drugu Node aplikaciju):
Što se tiče parametara koji su korišćeni za kreiranje servera, prepoznajemo:
- IP adresu (
127.0.0.1
- što je praktičnolocalhost
) - port (
process.env.port || 8080
) - statusni kod (
200
)
Što se tiče tehnikalija koje (možda) "ne prepoznajemo" na samom početku, osvrnimo se za sada na objekte req
i res
.
U pitanju su objekti koji sadrže:
- parametre zahteva koji je korisnik uputio (za šta je zadužen objekat
req
- skraćeno od "request") - detaljan odgovor servera (za šta je zadužen objekat
res
- skraćeno od "response")
Metoda pipe
(preko koje se direktno predaje odgovor klijentu, to jest, objekat res
), posebno je zanimljiva, jer upravo se preko navedene metode sadržaj HTML datoteke index.html
preusmerava u objekat res
(koji "nosi" sadržaj stranice koja će biti prikazana). *
Pri pozivu skripte preko konzolnog Node interpretatora, videli smo da se ne mora koristiti ekstenzija .js
(nismo morali ni do sada, ali, hteli smo da budemo precizni na samom početku), a ako posmatramo terminal, možemo steći utisak da program .... "stoji i ne radi ništa"?!
Međutim, kreirali smo Node aplikaciju koja (osim poruke koja se ispisuje na početku), ne komunicira sa krajnjim korisnicima preko konzole, već - preko internet browsera.
Ako otvorimo browser i unesemo adresu localhost:8080
, biće prikazana stranica koju smo kreirali preko funkcije formatiranjeHTMLDokumenta
(iz prvog primera).
Šta je REPL?
Sada kada vidimo da server 'funkcioniše' (u osnovnom smislu), osvrnimo se još jednom na tok komunikacije između klijenta i servera.
Program "osluškuje" mrežni saobraćaj preko porta 8080
, odnosno, proverava da li postoje zahtevi koji su upućeni serveru, i čim se pojavi zahtev, program proverava URL * i servira klijentu predviđeni sadržaj (u konkretnom slučaju, statičku HTML stranicu).
Nakon što se (određeni) zahtev korisnika obradi, aplikacija se vraća na "osluškivanje" (praktično: "očekivanje budućih zahteva").
Skraćenica REPL iz gornjeg naslova (Read-Evaluate-Print-Loop), odnosi se na prethodno opisani postupak:
- Read ("osluškivanje" konekcije)
- Evaluate (provera i obrada zahteva)
- Print (prikaz stranice, u slučaju da postoji pravilno primljen i obrađen zahtev)
- Loop (povratak na osluškivanje konekcije)
Sledeći koraci ....
Uvodni članak o okruženju Node.js zamislili smo samo kao kratak uvod u osnovne tehnikalije, i ovoga puta nećemo 'širiti diskusiju', međutim, budući da smo "načeli" priču o "Node serverima", svakako ćemo se u doglednoj budućnosti pozabaviti upravo temom pokretanja servera.
Koristićemo okruženje Express.js, koje predstavlja jednostavnu i funkcionalnu nadogradnju osnovnog okruženja (popularno: "micro framework"), definisaćemo rute i odgovore, predvidećemo mehanizme za obradu korisničkih grešaka, koristićemo šablone za kreiranje HTML-a i naravno - povezaćemo web aplikaciju sa bazom podataka (a bavićemo se i brojnim drugim tehnikalijama).
Pored "Express servera", teme kojima ćemo ubuduće takođe posvetiti pažnju, biće: razlike između PHP-a, Node.js-a i drugih back-end tehnologija (pri čemu ćemo se detaljno pozabaviti i PHP interpretatorom), asinhrono programiranje u JS-u, AJAX, Fetch API (a "negde usput" napravićemo i kratak osvrt na back-end tehnologije koje su zasnovane na Python-u).
Tema ima "podosta", ali, tim bolje ....