Menu

Node.js: jak JavaScript działa poza przeglądarką

Czym jest środowisko uruchomieniowe Node.js, czym różni się od przeglądarki i jakie podstawowe API (globalne obiekty, moduły, process, fs) umożliwiają JavaScript po stronie serwera.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Czym właściwie jest Node

Node.js to program, który instalujesz na komputerze i który uruchamia pliki JavaScript. To najprostszy opis i jest trafny. Gdy wpisujesz node script.js, Node czyta plik, przekazuje go silnikowi V8 od Google (silnikowi JavaScript z Chrome) i wykonuje, z dużą biblioteką dodatkowych API doklejoną do rzeczy, których V8 sam nie potrafi.

V8 umie wykonywać JavaScript. Nie umie otworzyć pliku, nasłuchiwać na gnieździe TCP, uruchomić procesu ani odczytać zmiennej środowiskowej. Node dostarcza to wszystko, napisane w C++, i udostępnia twojemu kodowi JavaScript jako wbudowane moduły.

node --version
node script.js

Node nie jest więc językiem. Nie jest też frameworkiem. To środowisko uruchomieniowe: V8 plus biblioteka standardowa plus system modułów plus pętla zdarzeń. To wszystko.

Pierwszy skrypt

Każdy plik .js jest poprawnym programem Node. Żadnego boilerplate'u, żadnej funkcji main:

console.log działa tak samo jak w przeglądarce. Template literals, Date, tablice, promisy: wszystkie znane ci funkcje języka pochodzą z V8 i zachowują się identycznie. W Node zmienia się to, co jest dostępne wokół języka.

Globalne obiekty dostępne tylko w Node

Przeglądarka ma window, document, localStorage, fetch. Node ma inny zestaw globalnych obiektów, dopasowany do roli środowiska po stronie serwera:

  • process to działający proces Node. Zawiera zmienne środowiskowe (process.env), argumenty wiersza poleceń (process.argv) i metody do zakończenia działania (process.exit(1)).
  • __filename i __dirname dają bezwzględną ścieżkę bieżącego pliku i jego folderu. (W modułach ES nie istnieją, zamiast nich używasz import.meta.url.)
  • global to obiekt najwyższego poziomu, odpowiednik window w Node.

Nie ma document ani window. Jeśli spróbujesz ich użyć, dostaniesz ReferenceError. To zwykle pierwszy sygnał, że biblioteka została napisana dla przeglądarki i nie zadziała w Node bez zmian.

Argumenty wiersza poleceń i środowisko

Duża część zastosowań Node, czyli narzędzia CLI, skrypty budujące i serwery, wymaga odczytu argumentów i zmiennych środowiskowych. Oba są dostępne w process:

process.argv to tablica: pierwsze dwa elementy to ścieżka do programu Node i ścieżka skryptu, więc właściwe argumenty zaczynają się od indeksu 2. process.env to zwykły obiekt ze zmiennymi środowiskowymi. Odczytywanie z niego NODE_ENV, PORT czy kluczy API to standardowa praktyka.

Wbudowane moduły

Node ma bibliotekę standardową dostępną przez require (CommonJS) lub import (ESM). Nazwy modułów zaczynają się od node:, żeby było jasne, że są wbudowane:

Te, po które będziesz sięgać najczęściej:

  • node:fs: odczyt i zapis plików. Wersja z async/await to node:fs/promises.
  • node:path: łączenie, rozwiązywanie i parsowanie ścieżek plików niezależnie od systemu.
  • node:http / node:https: budowanie serwerów HTTP i wysyłanie żądań.
  • node:url: parsowanie i tworzenie adresów URL.
  • node:os: informacje o komputerze, na którym działa program.
  • node:crypto: haszowanie, losowe bajty, szyfrowanie.

Tych modułów nie instalujesz. Są częścią Node. Wszystko inne pochodzi z npm.

Pętla zdarzeń w skrócie

Node uruchamia twój JavaScript w jednym wątku, a mimo to obsługuje wiele rzeczy naraz. Sztuczka polega na pętli zdarzeń. Gdy wywołujesz coś asynchronicznego, na przykład odczyt pliku, żądanie HTTP czy timer, Node przekazuje właściwą pracę systemowi operacyjnemu (lub swojej puli wątków) i wykonuje kod dalej. Gdy praca się kończy, callback trafia do kolejki, a pętla odbiera go, gdy bieżący kod skończy działanie.

Wypisana kolejność to 1, 4, 2, 3. Najpierw wykonuje się kod synchroniczny. Potem mikrozadania (rozwiązane promisy). Potem timery. Dlatego wolna pętla obciążająca procesor blokuje cały serwer: dla twojego kodu jest tylko jeden wątek. Współbieżność w Node dotyczy operacji wejścia/wyjścia, a nie obliczeń.

Malutki serwer HTTP

Nagroda za to wszystko: działający serwer WWW mieści się w kilku liniach:

Bez frameworka, bez zależności. createServer przyjmuje funkcję, która wykonuje się przy każdym żądaniu, a listen sprawia, że pętla zdarzeń zaczyna obsługiwać przychodzące połączenia. Prawdziwe aplikacje używają do tego Express lub Fastify, ale pod spodem to ten sam wbudowany moduł http.

Node kontra przeglądarka

Warto jasno powiedzieć, co się przenosi, a co nie:

Działa w obuTylko NodeTylko przeglądarka
Funkcje języka (klasy, promisy, async/await)fs, http, process, __dirnamewindow, document, DOM
console.logCommonJS require / dziwactwa ESM specyficzne dla NodelocalStorage, sessionStorage
fetch (Node 18+)Dostęp do systemu plików i gniazd sieciowychZdarzenia użytkownika, renderowanie
setTimeout, setIntervalProcesy potomne, strumienieHistory API, navigator

Nowoczesny Node przejął kilka API z przeglądarki, takich jak fetch, URL, AbortController czy structuredClone, więc różnica jest mniejsza niż kiedyś. Ale DOM do Node nie trafi, a system plików do przeglądarki też nie.

Node kontra Deno kontra Bun

Node jest domyślnym wyborem, ale nie jest już jedynym środowiskiem JavaScript. Alternatywy to Deno i Bun: Deno stworzył pierwotny twórca Node, a Bun nowszy zespół skupiony na szybkości. Uruchamiają JavaScript (i natywnie TypeScript), mają wbudowane narzędzia, takie jak test runnery i bundlery, i różnią się od Node obsługą modułów, uprawnień i instalacji pakietów.

Do nauki JavaScript Node wciąż jest miejscem, gdzie żyje dokumentacja, tutoriale i oferty pracy. Pojęcia takie jak pętla zdarzeń, moduły i wbudowane API niemal w całości przenoszą się na inne środowiska. Naucz się najpierw Node, a po pozostałe sięgnij, gdy projekt będzie tego wymagał.

Szybkie uruchamianie skryptów

Kilka sposobów na faktyczne wykonanie kodu podczas pracy:

# Run a file
node script.js

# Run a one-liner
node -e "console.log(2 ** 10)"

# Open the REPL (interactive prompt)
node

# Watch a file and re-run on save (Node 18.11+)
node --watch script.js

REPL to wygodny brudnopis, gdy chcesz sprawdzić, co zwraca dana metoda, bez tworzenia pliku. --watch przydaje się podczas pracy nad kodem: zapisujesz plik, a Node automatycznie uruchamia skrypt ponownie.

Dalej: obsługa błędów

Uruchamianie kodu to jedno, a radzenie sobie z sytuacją, gdy coś pójdzie nie tak, to drugie. Odczyty plików się nie udają, żądania HTTP przekraczają limit czasu, parsowanie JSON rzuca błędy. Następny rozdział dotyczy try/catch, typów błędów i wzorców radzenia sobie z tym, co się psuje, a w programie Node prędzej czy później psuje się wszystko.

Najczęściej zadawane pytania

Czym jest środowisko uruchomieniowe Node.js?

Node.js to program, który uruchamia JavaScript poza przeglądarką. Łączy silnik V8 od Google (ten sam, który wykonuje JS w Chrome) z warstwą w C++, która dostarcza API, jakich sam V8 nie ma: dostęp do systemu plików, sieć, procesy, timery. To połączenie pozwala budować w JavaScript serwery, narzędzia CLI i narzędzia do budowania projektów.

Czym Node różni się od przeglądarki?

Oba uruchamiają JavaScript, ale API wokół języka są inne. Przeglądarka daje ci window, document i DOM. Node daje ci process, fs, http, __dirname oraz ładowanie modułów CommonJS/ESM. W Node nie ma DOM, a w przeglądarce nie ma systemu plików: język jest wspólny, platforma nie.

Czy Node.js to framework czy środowisko uruchomieniowe?

Środowisko uruchomieniowe. Sam Node nie narzuca żadnej struktury aplikacji: po prostu wykonuje JavaScript i udostępnia API. Frameworki takie jak Express, Next.js czy NestJS są zbudowane na Node. Deno i Bun to alternatywne środowiska JavaScript, które konkurują z Node, ale pełnią tę samą rolę.

Czym jest pętla zdarzeń w Node?

Pętla zdarzeń (event loop) pozwala Node obsługiwać wiele rzeczy naraz w jednym wątku. Gdy wywołujesz coś asynchronicznego, na przykład odczyt pliku, żądanie HTTP czy setTimeout, Node przekazuje pracę systemowi i działa dalej. Gdy praca się kończy, jej callback trafia do kolejki, a pętla zdarzeń go odbiera. Dlatego fs.readFile nie blokuje reszty programu.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ