NodeJS

Czym jest Node.js

Środowisko uruchomieniowe JavaScript. Umożliwia uruchomienie kodu JavaScript poza przeglądarką. Ryan Dahl 2009.

Jednowątkowe (tak jak JS), sterowane wydarzeniami. Open Source, napisane w C++, wieloplatformowe: Windows, Mac i Linux.

W 2018 Dahl ogłosił powstanie alternatywy dla Node.js, z założenia nastawionej na bezpieczeństwo - V8, Rust i Tokio

Składniki

  • *Silnik V8- produkt Googla, znany z przeglądarki Chrome; serce Node.js
  • *npm- domyślny menedżer pakietów - i biblioteka npm; zarządza pakietami i zależnościami
  • *libuv- biblioteka udostępniająca funkcje systemu operacyjnego, m in zapewnia asynchroniczność - libuv | Cross-platform asynchronous I/O, operacje wejścia wyjścia, system plików, pętla zdarzeń
  • *REPL- (Read - Eval - Print - Loop) interfejs poleceń Node.js interaktywny terminal, dostęp do modułów podstawowych, uruchamia go polecenie node; online: Repl.it - Online Nodejs Editor and IDE
  • *moduły- dzielimy je na podstawowe (wystarczy require) i dostępne w pakietach (trzeba zainstalować)
    • *http- jeden z wielu standardowych modułów, ten jest odpowiedzialny za obsługę protokołu HTTP
  • *nodemon- nazwa to skrót od "Node.js monitor"; jest to osobny, nie będący częścią pakietu, program uruchamiany w trybie demona, niezbędny do dewelopingu; śledzi pliki i przy każdej modyfikacji restartuje Node.js. nodemon; trzeba go zainstalować osobno, wyjście ctrl+C

Moduły podstawowe (core modules) niektóre nie wymagają nawet require: console, setTimeout. Działają jak w JS, implementacje się różnią, ale działanie jest identyczne. 124/86

Innym popularnym menedżerem pakietów jest yarn.

Odpowiednikiem obiektu window w przeglądarce, w Node.js jest obiekt global. W REPLu this===global. Poza nim this === exports.

Silnik V8, tak samo jak JS, jest synchroniczny, to Node zapewnia asynchroniczność, LibUV zarządza zdarzeniami asynchronicznym.

  • *synchroniczne- (bieżące zadanie blokuje, reszta czeka); callstack stos wywołań obsługujący jednowątkowość.
  • *asynchroniczne- (wykonywane są po kolei, część odsyłana do pętli zdarzeń i czeka aż stos wywołań będzie pusty), najczęściej związane z callbackiem teraz często również z promisami i async/await; zwykle err i to co zwraca wyniki: error first callback; mówimy o tym pętla zdarzeń i nieblokujący model wejścia wyjścia (event loop i non-blocking I/O)

V8 JIT, w chwili wykonania JS jest optymalizowany i kompilowany do kodu maszynowego; zarządza pamięcią (garbage collection)

Czego nie ma w Node.js a jest tylko w przeglądarce: DOM, element window, cookie, fetch, alert.

Node pracuje z plikami (i systemem plików przez system operacyjny), bazami danych, backend, biblioteka modułów, praca z siecią.

Uruchomienie

Przy uruchomieniu przez init generator tworzy packet.json z informacjami o zainstalowanych pakietach; packet.lock.json nie do edycji, dla instalacji na nowo przez install

npm init

Nie trzeba odpowiadać na pytania. Z ^ aktualizacja tylko do wersji zgodnej z pierwszym numerem.

  • y automatycznie
  • -S pojawia się w zależnościach
  • install odtwarza na podstawie jsona
  • save dev w zależnościach developmentu (odwrotność: npm install --only=prod)
  • -g globalnie flaga
  • npm uninstall nazwa_modułu odinstalowanie

JSON: jak obiekt JS, ale klucz w cudzysłowie (nie apostrofie), a ostatni nie ma przecinka. Metody: parse i stringify.

Architekturą różni się od przeglądarki, ale posiada te same funkcje. Można uruchomić JS bezpośrednio w linii poleceń:

 node App.js

Nazwa App.js jest domyślna, ale zwyczajowa.

Moduły

Moduł to oddzielny program, element programu, który coś robi, wykonuje jakieś zadania. Może być plikiem, lub katalogiem z plikami. Jest używany wielokrotnie.

Kontaktuje się z innymi elementami programu przez API, który jest interfejsem eksportowanym i importowanym. Bez eksportu i importu moduły są niezależne, nie widzą się nawzajem i mają izolowany scope, zasięg. Wynika to z faktu, że w momencie uruchomienia są przez Node.js owijane w funkcję (wrapper) i mają wszystkie cechy funkcji, czyli np. własny zasięg (prywatny).

mod.wrapper

 (function (exports, require, module, __filename, __dirname) {
  // kod modułu
})()

Parametry dirname i filename są to dane lokalne. Zmieniają się zależnie od kontekstu, czyli miejsca w którym są wywoływane.

To dzięki funkcji require() łączącej moduły były one dostępne w Node.js na wiele lat zanim wprowadzono je do JS w ES6 w 2015. Require jest synchroniczna, bo czekamy na rezultat. Tak jak w poniższym przykładzie bez ścieżki dostępu odnosimy się tylko do modułów podstawowych, które są zaimplementowane jako funkcje.

 const http = require('http');

Powyżej składnia CommonJS, można też stosować nowszą ES6 (ES Modules)

 import http from 'http';

Początkowo exports i module.exports to to samo, oba prowadzą do tego samego pustego obiektu. Dlatego każdy moduł domyślnie coś zwraca, ma return z wrappera; jeżeli nie zadeklarujemy co, to będzie to pusty obiekt: { }. Kiedy tworzymy moduły sami musimy określić co z nich zwracamy. Kiedy tworzymy przypisanie module.exports się zmienia.

Moduły możemy podzielić na wbudowane (core modules), instalowane (z npm) i własne napisane przez nas. API to zbiór udostępnianych funkcji.

Niektóre moduły podstawowe

 fs.readFile('./file.txt', 'utf8', (err, file) => console.log(file))
 fs.access('ścieżka i nazwa pliku', callback)

 fs.rename('stara nazwa', 'nowa nazwa', callback)

 fs.readdir('ścieżka', (err, files) => {callback})

 fs.readfile('nazwa', 'kodowanie np utf8' domyslnie null czyli bufor, (err, data)=>{callback})

 fs.writeFile('nazwa', 'tresc' (err)=>{callback})

 fs.appendFile - jw

Domyślnie utf8 append write file

 path.join(__dirname, 'nazwapliku')

 path.parse('__filename')

 path.extname('nazwa pliku')

 path.isAbsolute('ścieżka i nazwa pliku')

 os.uptime()

 os.homedir()

Fetch

Promisy, pozytywne rozwiązanie przez kolejne then. Błąd obsługiwany jest przez catch. Jeżeli jest tylko jeden wiersz funkcji return nie jest potrzebny. Moduł node-fetch

const fetch = require("node-fetch");

const year = process.argv[2] || Math.floor(Math.random() - 2020);

fetch(\`http://numbersapi.com/$\{year}/year?json\`)
  .then(response => {
    console.log(response.status, response.ok);
    return response.json();
  })
  .then(data => console.log(data.text))
  .catch(error => console.log("Błąd", error));

Inne moduły tego typu: request i minimist.

Serwer

Żądanie zawiera: nazwę metody (domyślnie get), ścieżkę URL bez portu i hosta, wersję protokołu

Obiekt żądania:

  • req.url ścieżka URL
  • req.methods domyślnie GET
  • req.headers obiekt składający się z par nazwa wartość; np. {host : 'localhost:3000', connection: 'keep-alive'}

Obiekt odpowiedzi

  • res.writeHead() status odpowiedzi, i jej nagłówki (headers) w formie obiektu; np. writeHead(200, {'Content-Type':'application/json'})

  • res.statusCode kod odpowiedzi

  • res.write(/- zawartość */) treść odpowiedzi

  • res.end() informacja dla serwera "możesz wysyłać", zakończenie odpowiedzi, można tam zawrzeć treść zastępując res.write().

  • HTTP response status codes - kody odpowiedzi serwera

  • HTTP request methods - metody: GET, POST, ALL, DELETE itp

Serwer w Node.js

Najprościej

 const http = require("http");

 http
 .createServer((req, res) => {
 res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
 res.end("<h1>Hej, zażółć gęślą jaźń</h1>");
 })
 .listen(3000, "127.0.0.1");

Podzielony na elementy

 const http = require("http");

 const server = http.createServer();
 server.addListener("request", (req, res) => {
 res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
 res.end("no elo, zażółć gęślą jaźń");
 });
 server.listen(3000, "127.0.0.1");

Port można zapisać do zmiennej ${port}.

 const port = process.env.PORT || 3000;

W środku odpowiedzi logika asynchroniczna żeby nie blokować serwera. Routing to rozdzielenie odpowiedzi/zasobów w zależności od charakteru żądania. Przykład routingu:

 const http = require("http");
 const fs = require("fs");
 const path = require("path");
 const port = process.env.PORT || 3000;
 
 const users = [{ name: "Adam", id: 1 }, { name: "Ewa", id: 2 }];
 
 http
   .createServer((req, res) => {
     res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
     switch (req.url) {
       case "/":
         fs.readFile(path.join(__dirname, "index.html"), (err, page) => {
           if (err) res.end("Nie ma pliku index");
           else res.end(page);
         });
         break;
       case "/users":
         fs.readFile(path.join(__dirname, "users.html"), (err, page) => {
           if (err) res.end("Nie ma pliku users");
           else res.end(page);
         });
         break;
       case "/api":
         res.writeHead(200, {
           "Content-Type": "application/json; charset=utf-8"
         });
         const usersJSON = JSON.stringify(users);
         res.end(usersJSON);
         break;
       case "/code.js":
         res.writeHead(200, {
           "Content-Type": "application/javascript; charset=utf-8"
         });
         fs.readFile(path.join(__dirname, "./code.js"), (err, page) => {
           if (err) res.end("Nie ma pliku");
           else res.end(page);
         });
         break;
       default:
         res.end("strona nie istnieje");
     }
   })
   .listen(port, "127.0.0.1", () => {
    console.log(\`Serwer nasłuchuje na porcie: $\{port}\`);
   });

Odnośniki

Strony

Kursy

Narzędzia

Artykuły

Web Accessibility

Youtube

Deploying

Typescript

NestJS

Frameworki

Testy

Web Accessibility

Deno

API

API YT

REST API

Postman