Node.js

Mis on Node.js

Node.js on serveripoolne JavaScripti täitmiskeskkond (runtime), mis võimaldab käivitada JavaScripti väljaspool veebibrauserit. See tähendab, et JavaScripti saab kasutada mitte ainult kasutajaliidese loomiseks, vaid ka serverite, API-de ja rakendusloogika kirjutamiseks.

Oluline on rõhutada järgmist:

  • Node.js ei ole programmeerimiskeel
  • Node.js ei ole raamistik
  • Programmeerimiskeel on JavaScript
  • Node.js on keskkond, kus JavaScript serveris töötab

Tehniliselt koosneb Node.js mitmest komponendist:

  • Google’i V8 JavaScripti mootor (kirjutatud C++ keeles)
  • libuv platvormikiht asünkroonse I/O jaoks
  • Node.js-i enda tuumik, mis on suures osas JavaScriptis

See kombinatsioon võimaldab JavaScriptil suhelda operatsioonisüsteemi, võrgu ja failisüsteemiga.

Node.js arhitektuur ja tööpõhimõte

Node.js kasutab sündmuspõhist ja mitteblokeerivat arhitektuuri, mida tuntakse single-threaded event loop mudelina. See erineb oluliselt traditsioonilisest mitmelõimelisest serverimudelist.

Traditsiooniline mitmelõimeline server:

  • iga päring saab oma lõime
  • blokeerivad operatsioonid hoiavad lõime kinni
  • suur koormus tähendab palju lõime ja suurt mälukulu

Node.js töötab teistmoodi:

  • rakenduse loogika jookseb ühes põhilõimes
  • sissetulevad päringud pannakse järjekorda
  • event loop töötleb päringuid ükshaaval
  • blokeerivad I/O-operatsioonid antakse üle taustalõimedele
  • event loop jätkab teiste päringute teenindamist

Tulemuseks on väiksem ressursikasutus ja parem skaleeritavus I/O-mahukate rakenduste puhul.

Asünkroonne programmeerimine Node.js-is

Node.js eeldab asünkroonset programmeerimist. See tähendab, et aeglased operatsioonid ei peata kogu rakenduse tööd.

Praktikas kasutatakse:

  • callback’e
  • promise’e
  • async/await süntaksit

Async/await muudab koodi loetavaks, kuid ei muuda seda sünkroonseks. Rakendus jääb mitteblokeerivaks, mis on Node.js-i tööpõhimõtte alus.

Kus ja milleks Node.js-i kasutatakse

Node.js sobib parimal juhul rakendustele kus on palju andmesuhtlust ning suhteliselt vähe raskeid arvutusi.

Levinumad kasutusvaldkonnad:

  • REST API-d ja backend-teenused
  • serveripõhised veebirakendused
  • reaalajasüsteemid (chat, teavitused, socketid)
  • IoT-lahendused
  • arendustööriistad ja skriptid

Node.js ei ole hea valik CPU-mahukate arvutuste jaoks, kuid see ei ole ka selle eesmärk.

Node.js-i peamised eelised

  • sama keel frontend’is ja backend’is
  • väga suur npm pakettide valik
  • hea jõudlus paljude spontentuaalsete päringute korral
  • lihtne HTTP- ja API-põhine arendus
  • sobivus pilve- ja mikroteenuste arhitektuuridele

Node.js-i piirangud ja riskid

  • CPU-mahukad tööd võivad blokeerida event loop’i
  • halb ahritektuur võib mõjutada kogu serverit
  • nõuab arendajalt asünkroonse mõtteviisi mõistmist

Arenduskeskkonna ettevalmistus:

  • Visual Studio code(ja extensionid)
    • Prettier extension – vormindab koodi automaatselt ühtsesse stiili; parandab loetavust ega mõjuta programmi loogikat
    • DotENV extension – lisab toe .env failide süntaksi esiletõstmiseks; kasulik siis, kui hakatakse kasutama keskkonnamuutujaid
    • JavaScript (ES6) code snippets – kiirendab JavaScripti kirjutamist; kasulik algajale, ei peida loogikat
    • Database Client JDBC – võimaldab MariaDB andmebaasi vaadata, päringuid käivitada ja andmeid kontrollida otse VS Code’is
  • Git & GitHub
  • XAMPP (MariaDB)

Esimese Node.js programmi loomine

Node.js on JavaScripti fail seega lõin faili node01.js ja lisasin sinna alloleva koodi:

console.log("Tere maailm!");

console.log() väljastab Node.js-is teksti terminali.

Node.js programmi käivitamine

Programmi käivitamiseks kasutatakse Node.js käsurea käsku node. Terminalis käivita programm käsuga:

node node01.js

Node.js lubab JavaScripti faili käivitada ka ilma .js laiendit kirjutamata:

node node01

Õppimise algfaasis kasutan täisfailinime, et failid oleksid selgelt eristatavad.

Meeldetuletus JavaScriptist

Enne Node.js rakenduste arendamisega edasi liikumist kordan üle mõned JavaScripti konstruktsioonid, mida Node.js koodis kasutatakse pidevalt. Vastasel juhul muutub Node.js kood raskesti loetavaks ja ebamugavaks kirjutada.

Noolefunktsioonid (Arrow functions)

Noolefunktsioonid on lühem ja tänapäevasem viis funktsioonide kirjutamiseks. Node.js koodis kasutatakse neid sageli callback’ide, abifunktsioonide ja lihtsa loogika puhul.

const tervitus = (nimi) => {
  return "Tere " + nimi;
};

Üherealise funktsiooni lühivorm:

const tervitus = nimi => "Tere " + nimi;

Noolefunktsioonid on levinud, kuna süntaks on lühem ja sobib hästi väikeste funktsioonide jaoks.

Teksti vormindamine (template literals)

Node.js rakendustes kasutatakse sageli dünaamilisi tekste, näiteks logisid ja veateateid. Template literal’id muudavad sellise teksti koostamise selgemaks.

const nimi = "Juhan";
console.log(`Tere ${nimi}!`);

Template literal’id on loetavamad kui stringide ühendamine + märgiga ning võimaldavad kirjutada ka mitmerealisi stringe.

Objekti ja massiivi destruktureerimine

Node.js koodis töötatakse pidevalt objektide ja konfiguratsioonidega. Destruktureerimine võimaldab vajalikud väärtused objektist või massiivist otse kätte saada.

const config = {
host: "localhost",
user: "root",
password: "secret"
};

const { host, user } = config;

Massiivide puhul:

const [esimene, teine] = ["a", "b", "c"];

Destruktureerimist kasutan sageli configides, functionites, paramites ja vastusobjektide töötlemisel.

Vaikimisi väärtused (default values)

Node.js tuleb arvestada, et alati ei pruugi kõik väärtused olemas olla. Selleks kasutame vaikimisi väärtusi et vältida undefined olukordi.

const tervitus = (nimi = "külaline") => {
  console.log(`Tere ${nimi}`);
};

Asünkroonne süntaks (async ja await)

Node.js-is on failide lugemine, andmebaasipäringud ja võrgutöö asünkroonsed.

const loeFail = async () => {
const sisu = await fs.promises.readFile("test.txt", "utf8");
console.log(sisu);
};

Süntaks peaks olema arusaadav ka siis, kui teemat veel sügavuti ei käsitleta.

Vigade käsitlemine (try...catch)

Vigade pyydmis syntax:

try {
teeMidagi();
} catch (error) {
console.error(error.message);
}

Node.js globaalsed objektid (global objects)