Node.js
Node.js jest środowiskiem uruchomieniowym JavaScript poza przeglądarką. Przy czytaniu projektu najważniejsze są package.json, lockfile, moduły, skrypty npm, konfiguracja środowiska oraz sposób uruchomienia procesu w development i production.
Dla nowych projektów wybieraj wspieraną linię LTS zamiast przywiązywać projekt do przypadkowej wersji Current.
Powiązane tematy: JavaScript - kompendium, API i integracje systemów, SQL i PostgreSQL dla developera, Docker i Testowanie oprogramowania.
Cel tego dokumentu: nie nauczyć Cię programować w Node.js od zera, tylko sprawić, żebyś potrafił:
- sklonować cudzy projekt,
- rozpoznać jego strukturę,
- zrozumieć podstawową składnię,
- wiedzieć skąd biorą się zależności,
- uruchomić projekt lokalnie,
- zbudować go,
- debugować podstawowe problemy,
- uruchomić testy,
- wdrożyć aplikację na serwer,
- zorientować się, co się właściwie dzieje w kodzie.
1. Czym właściwie jest Node.js?
Node.js to środowisko pozwalające uruchamiać JavaScript poza przeglądarką.
W przeglądarce JavaScript korzysta między innymi z:
- DOM,
window,document,- zdarzeń przeglądarki,
- API przeglądarkowych.
Node.js daje JavaScriptowi inne możliwości:
- dostęp do plików,
- dostęp do sieci,
- uruchamianie serwerów HTTP,
- procesy systemowe,
- zmienne środowiskowe,
- komunikację z bazami danych,
- obsługę socketów,
- skrypty CLI.
Przykład:
console.log("Hello from Node.js");
Uruchomienie:
node app.js
2. Node.js, npm, npx - co jest czym?
Node.js
Interpreter / runtime JavaScript.
Sprawdzenie wersji:
node --version
lub:
node -v
npm
Node Package Manager.
Instaluje zależności projektu.
npm --version
Typowe polecenie:
npm install
npm czyta plik:
package.json
i instaluje potrzebne paczki.
npx
Uruchamia program znajdujący się w paczce npm bez konieczności instalowania go globalnie.
Przykład:
npx eslint .
albo:
npx vite
3. Instalacja Node.js
Na systemach developerskich najlepiej unikać przypadkowych, bardzo starych wersji Node dostarczanych przez repozytorium systemowe.
Najwygodniejsze rozwiązania:
- nvm,
- fnm,
- Volta.
Najbardziej klasyczny jest nvm.
Po instalacji można wykonać:
nvm install --lts
Następnie:
node -v
npm -v
Lista zainstalowanych wersji:
nvm list
Zmiana wersji:
nvm use 22
Jeżeli projekt ma plik:
.nvmrc
można wykonać:
nvm use
4. Projekt Node.js - najważniejsze pliki
Typowy projekt może wyglądać tak:
my-project/
├── package.json
├── package-lock.json
├── node_modules/
├── src/
│ ├── index.js
│ ├── server.js
│ ├── routes/
│ ├── controllers/
│ └── services/
├── tests/
├── public/
├── .env
├── .env.example
├── .gitignore
└── README.md
Najważniejsze pliki:
package.json
Serce projektu Node.
Przykład:
{
"name": "my-app",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "node --watch src/index.js",
"start": "node src/index.js",
"test": "vitest",
"lint": "eslint ."
},
"dependencies": {
"express": "^5.0.0"
},
"devDependencies": {
"eslint": "^9.0.0",
"vitest": "^3.0.0"
}
}
Z tego pliku można bardzo szybko dowiedzieć się:
- jak projekt się nazywa,
- jaką ma wersję,
- jakie ma zależności,
- jakie polecenia można uruchamiać,
- czy używa ES Modules,
- czego używa do testów,
- czego używa do developmentu.
5. Sekcja scripts w package.json
To pierwsze miejsce, które powinieneś sprawdzić po sklonowaniu projektu.
Przykład:
"scripts": {
"dev": "vite",
"build": "vite build",
"start": "node dist/server.js",
"test": "vitest",
"lint": "eslint ."
}
Uruchomienie:
npm run dev
npm run build
npm run test
Wyjątkiem jest:
npm start
które może być uruchamiane bez run.
Lista dostępnych skryptów:
npm run
6. package-lock.json
Plik:
package-lock.json
zapamiętuje dokładne wersje zależności.
Przykład:
"express": "5.1.0"
Dzięki temu dwie osoby instalujące projekt powinny dostać praktycznie ten sam zestaw paczek.
Nie kasuj package-lock.json bez powodu.
7. node_modules
Po wykonaniu:
npm install
powstaje katalog:
node_modules/
Może zawierać dziesiątki albo tysiące paczek.
Nigdy nie wrzuca się go do Git.
.gitignore powinien zawierać:
node_modules/
Jeżeli coś dziwnie zachowuje się po zmianie zależności, czasami pomaga:
rm -rf node_modules
npm install
Jeszcze lepiej dla powtarzalnych instalacji:
npm ci
8. npm install kontra npm ci
npm install
npm install
Instaluje zależności.
Może również aktualizować package-lock.json.
Używane zwykle podczas developmentu.
npm ci
npm ci
Instaluje dokładnie wersje zapisane w package-lock.json.
Wymaga istniejącego lockfile.
Najlepsze dla:
- CI/CD,
- serwerów,
- Dockerfile,
- powtarzalnych buildów.
9. dependencies i devDependencies
W package.json:
"dependencies": {
"express": "^5.0.0"
}
To biblioteki wymagane do działania aplikacji.
Przykłady:
- Express,
- Fastify,
- PostgreSQL driver,
- Redis client.
"devDependencies": {
"eslint": "^9.0.0",
"vitest": "^3.0.0"
}
Potrzebne głównie podczas tworzenia aplikacji.
Przykłady:
- test framework,
- linter,
- bundler,
- formatter,
- TypeScript compiler.
10. Instalowanie paczek
Biblioteka runtime:
npm install express
Biblioteka developerska:
npm install --save-dev eslint
lub krócej:
npm i -D eslint
Usunięcie:
npm uninstall express
11. Wersjonowanie paczek
Przykład:
"express": "^5.1.0"
Znaki mają znaczenie.
Dokładna wersja
5.1.0
Tylko ta wersja.
Caret
^5.1.0
Pozwala zwykle aktualizować wersje minor i patch:
5.x.x
Tilde
~5.1.0
Pozwala głównie na aktualizacje patch:
5.1.x
12. Aktualizacja paczek
Sprawdzenie starych paczek:
npm outdated
Aktualizacja zgodnie z zakresem zapisanym w package.json:
npm update
Audyt bezpieczeństwa:
npm audit
Automatyczna próba naprawy:
npm audit fix
Nie stosuj bezmyślnie:
npm audit fix --force
bo może przeskoczyć na niekompatybilne wersje bibliotek.
13. JavaScript potrzebny do czytania Node.js
Zmienna let
let counter = 1;
counter = 2;
Wartość może się zmienić.
Stała const
const port = 3000;
Nie można przypisać nowej wartości do tej zmiennej.
W nowoczesnym JS najczęściej zobaczysz:
const
a dopiero gdy wartość musi się zmieniać:
let
var
Starsza forma:
var name = "Anna";
Obecnie zwykle się jej unika.
Jeżeli widzisz dużo var, prawdopodobnie patrzysz na starszy kod.
14. Typy danych
JavaScript jest dynamicznie typowany.
const name = "Anna";
const age = 46;
const active = true;
const nothing = null;
const missing = undefined;
Podstawowe typy:
string
number
boolean
null
undefined
bigint
symbol
object
15. Stringi
const name = "Anna";
lub:
const name = 'Anna';
Template string:
const text = `Hello ${name}`;
To bardzo częsta konstrukcja.
16. Tablice
const users = ["Anna", "Anna", "Jan"];
Dostęp:
users[0]
Rezultat:
Anna
Dodanie:
users.push("Marek");
Iteracja:
for (const user of users) {
console.log(user);
}
17. Obiekty
Bardzo ważne w JavaScript.
const user = {
name: "Anna",
age: 46,
active: true
};
Dostęp:
user.name
lub:
user["name"]
18. Destrukturyzacja
Bardzo częsta konstrukcja.
Zamiast:
const name = user.name;
const age = user.age;
można:
const { name, age } = user;
W funkcjach często zobaczysz:
function createUser({ name, email }) {
// ...
}
19. Spread operator
const newUser = {
...user,
active: false
};
Znaczy mniej więcej:
weź pola z
user, a następnie nadpiszactive.
Tablice:
const allUsers = [...users, "Adam"];
20. Funkcje
Klasyczna funkcja:
function add(a, b) {
return a + b;
}
Wywołanie:
add(2, 3);
21. Arrow functions
Bardzo popularne:
const add = (a, b) => {
return a + b;
};
Krótka forma:
const add = (a, b) => a + b;
Jedna zmienna:
const square = x => x * x;
22. Warunki
if (user.active) {
console.log("active");
}
if (age >= 18) {
console.log("adult");
} else {
console.log("minor");
}
23. Porównania
Preferuj:
===
zamiast:
==
Przykład:
5 === "5"
wynik:
false
Natomiast:
5 == "5"
może dać:
true
przez automatyczną konwersję typów.
24. Operatory logiczne
AND:
a && b
OR:
a || b
NOT:
!a
Przykład:
if (user && user.active) {
// ...
}
25. Optional chaining
Bardzo użyteczna składnia:
user?.profile?.email
Jeżeli któryś element nie istnieje, kod nie wywali od razu wyjątku.
Bez tego:
user.profile.email
może się wywalić, jeśli profile jest undefined.
26. Nullish coalescing
const port = process.env.PORT ?? 3000;
Jeżeli PORT jest null lub undefined, użyje:
3000
27. Operator warunkowy
const status = active ? "online" : "offline";
To krótka forma:
if (active) {
status = "online";
} else {
status = "offline";
}
28. Pętle
for (let i = 0; i < 10; i++) {
console.log(i);
}
Częściej w nowoczesnym JS:
for (const user of users) {
console.log(user);
}
29. map, filter, find
Bardzo częste w kodzie JS.
map
const names = users.map(user => user.name);
Tworzy nową tablicę.
filter
const activeUsers = users.filter(user => user.active);
Filtruje elementy.
find
const admin = users.find(user => user.role === "admin");
Zwraca pierwszy pasujący element.
30. Moduły
Kod Node zwykle podzielony jest na wiele plików.
Są dwa główne systemy modułów.
31. CommonJS
Starszy system Node.
Import:
const express = require("express");
Eksport:
module.exports = something;
lub:
exports.foo = foo;
32. ES Modules
Nowoczesny styl.
Import:
import express from "express";
Eksport:
export function foo() {
}
lub:
export default foo;
W package.json często zobaczysz:
"type": "module"
To sygnał, że projekt używa ES Modules.
33. import - jak go czytać
import express from "express";
Import domyślny.
import { readFile } from "node:fs/promises";
Import nazwany.
import * as fs from "node:fs";
Import całego modułu.
import config from "./config.js";
Import lokalnego pliku.
34. Moduły wbudowane Node.js
Node ma wiele własnych modułów.
Przykłady:
node:fs
node:path
node:http
node:https
node:os
node:crypto
node:events
node:stream
node:url
node:process
node:child_process
Przykład:
import fs from "node:fs";
Jeżeli import zaczyna się od:
node:
to biblioteka jest częścią Node.js.
35. Praca z plikami
Przykład:
import { readFile } from "node:fs/promises";
const text = await readFile("config.json", "utf8");
Zapis:
import { writeFile } from "node:fs/promises";
await writeFile("output.txt", "hello");
36. Asynchroniczność - klucz do Node.js
Node bardzo intensywnie korzysta z operacji asynchronicznych.
Przykład:
const data = await readFile("file.txt", "utf8");
Program może wykonywać inne rzeczy, kiedy czeka na:
- dysk,
- bazę danych,
- sieć,
- HTTP,
- Redis,
- API.
37. Promise
Funkcja może zwrócić obietnicę przyszłego wyniku.
fetch(url)
zwraca Promise.
Można użyć:
fetch(url)
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
38. async / await
Czytelniejsza forma:
async function loadData() {
const response = await fetch(url);
const data = await response.json();
return data;
}
Jeżeli widzisz:
await
to kod czeka na zakończenie operacji asynchronicznej.
39. Obsługa błędów
Klasycznie:
try {
const data = await loadData();
} catch (error) {
console.error(error);
}
Jeżeli szukasz problemu, bardzo często warto znaleźć:
try
catch
throw
40. throw
Ręczne zgłoszenie błędu:
throw new Error("User not found");
41. Event loop - wystarczy tyle
Node nie tworzy osobnego systemowego wątku dla każdego requestu.
Podstawowy model wygląda mniej więcej tak:
request
|
v
Node.js
|
+--> zleca I/O
|
+--> obsługuje inne rzeczy
|
v
callback / Promise
Dlatego Node dobrze nadaje się do aplikacji wykonujących dużo:
- requestów HTTP,
- operacji bazodanowych,
- komunikacji sieciowej,
- I/O.
Gorzej radzi sobie z ciężkimi obliczeniami CPU, jeżeli wykonuje się je bezpośrednio w głównym wątku.
42. process
Globalny obiekt Node.
Przykład:
console.log(process.pid);
Argumenty programu:
console.log(process.argv);
Wyjście:
process.exit(1);
43. Zmienne środowiskowe
Jedna z najważniejszych rzeczy w deploymentach.
const port = process.env.PORT;
Shell:
export PORT=3000
node server.js
lub:
PORT=3000 node server.js
44. Plik .env
Bardzo wiele projektów posiada:
.env
Przykład:
PORT=3000
DATABASE_URL=postgres://user:password@localhost/app
OPENAI_API_KEY=...
Nigdy nie commituj sekretów.
.gitignore:
.env
Projekt często dostarcza:
.env.example
Przykład:
PORT=
DATABASE_URL=
OPENAI_API_KEY=
45. Uruchomienie po sklonowaniu projektu
Standardowy workflow:
git clone https://github.com/example/app.git
cd app
Sprawdzenie:
ls
Następnie przeczytaj:
README.md
package.json
.env.example
Instalacja:
npm ci
Jeżeli lockfile nie istnieje:
npm install
Potem:
npm run
Sprawdź dostępne skrypty.
Najczęściej:
npm run dev
lub:
npm start
46. Jak znaleźć punkt wejścia aplikacji
Sprawdź package.json.
Może być:
"main": "src/index.js"
lub skrypt:
"start": "node src/server.js"
Wtedy wiadomo, że aplikacja zaczyna się tutaj:
src/server.js
Szukaj nazw:
index.js
server.js
app.js
main.js
cli.js
47. Jak czytać projekt Node.js
Nie czytaj wszystkich plików po kolei.
Najlepsza kolejność:
README.mdpackage.json- główny plik aplikacji
- konfiguracja
- routing
- logika biznesowa
- dostęp do danych
- testy
Typowy backend:
request
|
v
route
|
v
controller
|
v
service
|
v
repository / database
48. Typowa architektura Express
src/
├── app.js
├── server.js
├── routes/
├── controllers/
├── services/
├── middleware/
├── models/
└── config/
Znaczenie:
routes
Definicje endpointów.
Przykład:
router.get("/users", getUsers);
controllers
Obsługa requestu HTTP.
Przykład:
async function getUsers(req, res) {
// ...
}
services
Logika biznesowa.
userService.createUser(...)
models
Modele danych.
middleware
Kod uruchamiany pomiędzy requestem a właściwym handlerem.
Przykłady:
- autoryzacja,
- logowanie,
- CORS,
- parsowanie danych,
- rate limiting.
49. req i res
W Express:
app.get("/hello", (req, res) => {
res.json({ message: "hello" });
});
req:
request
czyli żądanie klienta.
res:
response
czyli odpowiedź serwera.
50. Parametry URL
Endpoint:
/users/123
Kod:
app.get("/users/:id", (req, res) => {
console.log(req.params.id);
});
51. Query string
URL:
/users?page=2
Kod:
req.query.page
52. Body requestu
JSON:
{
"name": "Anna"
}
Kod:
req.body.name
53. Status HTTP
res.status(404).json({
error: "Not found"
});
Typowe:
200 OK
201 Created
204 No Content
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
409 Conflict
500 Internal Server Error
54. Backend Node i frontend
Node nie oznacza automatycznie backendu.
Node może służyć również do:
- budowania Reacta,
- budowania Vue,
- budowania Angulara,
- uruchamiania Vite,
- narzędzi developerskich,
- lintowania,
- testowania.
Przykład:
React source
|
v
Vite
|
v
Node.js
|
v
dist/
Po buildzie Node może już wcale nie być potrzebny.
55. Czy Node.js się kompiluje?
To zależy.
Sam kod JavaScript zazwyczaj nie jest klasycznie kompilowany jak C czy Go.
Uruchamiasz:
node app.js
Node wykonuje kod przez silnik V8.
Ale projekt może posiadać etap:
npm run build
który może wykonywać:
- transpiling,
- bundling,
- minifikację,
- kompilację TypeScript,
- generowanie assetów,
- budowanie frontendu.
56. TypeScript
Bardzo częsty w projektach Node.
Pliki:
.ts
.tsx
TypeScript dodaje statyczne typowanie.
Przykład:
const age: number = 46;
TypeScript jest zwykle zamieniany na JavaScript.
src/
server.ts
|
v
npm run build
|
v
dist/
server.js
57. tsconfig.json
Konfiguracja TypeScript.
Przykład:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"outDir": "dist",
"strict": true
}
}
Jeżeli widzisz:
tsconfig.json
projekt korzysta z TypeScript.
58. Build projektu
Najpierw:
npm ci
Potem:
npm run build
Sprawdź, czy powstał katalog:
dist/
lub:
build/
Następnie zazwyczaj:
npm start
59. Development kontra production
Development:
npm run dev
Może uruchamiać:
- watcher,
- hot reload,
- sourcemapy,
- szczegółowe logi.
Production:
npm start
lub:
NODE_ENV=production npm start
60. NODE_ENV
Popularna zmienna:
NODE_ENV=production
Kod:
if (process.env.NODE_ENV === "production") {
// ...
}
Typowe wartości:
development
test
production
61. Watch mode
Node może automatycznie restartować aplikację po zmianie pliku.
Przykład:
node --watch src/index.js
Starsze projekty często używają:
nodemon
62. Debugowanie najprostsze
Najważniejsza metoda:
console.log(variable);
Lepsze:
console.log({ variable });
Błędy:
console.error(error);
Ostrzeżenia:
console.warn(message);
63. Debugger Node
Uruchomienie:
node --inspect src/index.js
lub zatrzymanie przed wykonaniem kodu:
node --inspect-brk src/index.js
Można potem podłączyć:
- Chrome DevTools,
- VS Code.
64. debugger
W kodzie można wstawić:
debugger;
Debugger zatrzyma wykonanie w tym miejscu.
65. Stack trace
Przykład błędu:
TypeError: Cannot read properties of undefined
at createUser (/app/services/user.js:42:17)
at handler (/app/routes/users.js:18:5)
Czytaj od góry.
Najważniejsza informacja:
/app/services/user.js:42:17
czyli:
plik
linia
kolumna
66. Linter
Najpopularniejszy:
ESLint
Uruchomienie:
npm run lint
lub:
npx eslint .
Linter wykrywa między innymi:
- błędy składni,
- podejrzany kod,
- nieużywane zmienne,
- naruszenia standardów projektu.
67. Formatter
Bardzo popularny:
Prettier
Przykład:
npx prettier --check .
Formatowanie:
npx prettier --write .
68. Testy
Najczęstsze frameworki:
- Vitest,
- Jest,
- Mocha,
- Node test runner.
Najpierw:
npm test
albo:
npm run test
69. Wbudowany test runner Node
Przykład:
import test from "node:test";
import assert from "node:assert";
test("2 + 2", () => {
assert.equal(2 + 2, 4);
});
Uruchomienie:
node --test
70. Jak znaleźć testy
Szukaj:
test/
tests/
__tests__/
lub plików:
*.test.js
*.spec.js
*.test.ts
*.spec.ts
Testy są świetną dokumentacją zachowania aplikacji.
71. Najważniejsze narzędzia ecosystemu
Express
Klasyczny framework HTTP.
Fastify
Nowoczesny i szybki framework backendowy.
NestJS
Duży framework aplikacyjny.
Struktura przypomina rozwiązania enterprise.
Vite
Build tool dla frontendu.
React
Biblioteka UI.
Vue
Framework UI.
Angular
Rozbudowany framework frontendowy.
Prisma
ORM / narzędzie do pracy z bazami.
Sequelize
ORM.
Drizzle
Nowoczesny ORM / query builder.
72. JSON
W Node spotkasz JSON wszędzie.
{
"name": "Anna",
"active": true
}
JavaScript:
const text = JSON.stringify(object);
JSON -> obiekt:
const object = JSON.parse(text);
73. npm, yarn, pnpm
Nie każdy projekt używa npm.
npm
Plik:
package-lock.json
yarn
Plik:
yarn.lock
pnpm
Plik:
pnpm-lock.yaml
Nie mieszaj menedżerów bez potrzeby.
Jeżeli projekt ma:
pnpm-lock.yaml
używaj pnpm.
74. Corepack
Node może współpracować z Corepack do zarządzania Yarn/pnpm.
Projekt może posiadać w package.json:
"packageManager": "pnpm@10.0.0"
To dobra wskazówka, czego użyć.
75. Jak rozpoznać technologię projektu
Express
"express": "..."
React
"react": "..."
Vue
"vue": "..."
Angular
"@angular/core": "..."
Vite
"vite": "..."
Next.js
"next": "..."
NestJS
"@nestjs/core": "..."
76. npm run build - co naprawdę robi?
Sprawdź:
"scripts": {
"build": "tsc"
}
oznacza TypeScript compiler.
"build": "vite build"
oznacza build Vite.
"build": "webpack"
oznacza Webpack.
"build": "next build"
oznacza Next.js.
77. Environment produkcyjny
Typowy backend Node potrzebuje:
Node.js
aplikacja
node_modules
config / env
baza danych
reverse proxy
manager procesu
78. Najprostszy deployment na Debianie
Załóżmy:
/opt/myapp
Klon:
sudo git clone https://github.com/example/myapp.git /opt/myapp
Przejście:
cd /opt/myapp
Instalacja:
npm ci
Build:
npm run build
Start testowy:
npm start
79. Nie uruchamiaj produkcji przez terminal
Nie rób:
ssh server
npm start
i zostawienia aplikacji w terminalu.
Po zamknięciu SSH proces może zniknąć.
Użyj:
- systemd,
- Dockera,
- ewentualnie PM2.
80. systemd
Dobre rozwiązanie dla jednej aplikacji Node.
Plik:
/etc/systemd/system/myapp.service
Przykład:
[Unit]
Description=My Node App
After=network.target
[Service]
Type=simple
User=myapp
WorkingDirectory=/opt/myapp
Environment=NODE_ENV=production
EnvironmentFile=/etc/myapp.env
ExecStart=/usr/bin/node /opt/myapp/dist/server.js
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
81. Uruchomienie systemd
Po dodaniu service:
sudo systemctl daemon-reload
Start:
sudo systemctl start myapp
Autostart:
sudo systemctl enable myapp
Status:
sudo systemctl status myapp
Logi:
journalctl -u myapp
Live:
journalctl -u myapp -f
82. Nginx jako reverse proxy
Aplikacja Node może słuchać lokalnie:
127.0.0.1:3000
Nginx wystawia ją na świat:
https://example.com
Schemat:
Internet
|
v
Nginx :443
|
v
Node :3000
Przykład:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
83. Port aplikacji
Typowy kod:
const port = process.env.PORT ?? 3000;
app.listen(port);
Produkcja:
PORT=3000
Aplikacji nie musisz wystawiać bezpośrednio na port 80/443.
Od tego jest Nginx.
84. PM2
Popularny manager procesów Node.
Instalacja:
npm install -g pm2
Start:
pm2 start dist/server.js --name myapp
Lista:
pm2 list
Logi:
pm2 logs myapp
Restart:
pm2 restart myapp
PM2 jest wygodny, ale na klasycznym Debianie często wystarcza zwykły systemd.
85. Deployment przez Docker
Typowy Dockerfile:
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
CMD ["npm", "start"]
Build:
docker build -t myapp .
Uruchomienie:
docker run -d \
--name myapp \
-p 3000:3000 \
--env-file .env \
myapp
86. Multi-stage Docker build
Lepszy model dla projektów wymagających kompilacji.
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
ENV NODE_ENV=production
CMD ["node", "dist/server.js"]
Zaletą jest mniejszy finalny obraz.
87. .dockerignore
Przykład:
node_modules
.git
.env
npm-debug.log
coverage
88. Deployment statycznego frontendu
Projekt React/Vue może po:
npm run build
utworzyć:
dist/
Jeżeli wynik to statyczne:
HTML
CSS
JS
assets
nie trzeba uruchamiać Node na produkcji.
Można skopiować dist/ do:
/var/www/example.com
i obsługiwać przez Nginx.
89. Typowy deployment backendu
Git
|
v
npm ci
|
v
npm run build
|
v
dist/
|
v
systemd / Docker
|
v
127.0.0.1:3000
|
v
Nginx
|
v
HTTPS
90. Deployment ręczny po aktualizacji
Przykład:
cd /opt/myapp
git pull
npm ci
npm run build
sudo systemctl restart myapp
Potem:
sudo systemctl status myapp
i:
journalctl -u myapp -n 100
91. Prosty skrypt deploy
#!/usr/bin/env bash
set -e
cd /opt/myapp
git pull
npm ci
npm run build
sudo systemctl restart myapp
sudo systemctl status myapp --no-pager
92. CI/CD
Automatyczny deployment może wyglądać tak:
git push
|
v
GitHub
|
v
GitHub Actions
|
+--> npm ci
+--> npm test
+--> npm run build
|
v
deployment
93. Podstawowy GitHub Actions
name: Node CI
on:
push:
branches:
- main
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm test
- run: npm run build
94. Logi
Najprościej:
console.log("server started");
W większych projektach spotkasz:
- Pino,
- Winston.
Przykładowy wpis JSON:
{
"level": "info",
"message": "server started",
"port": 3000
}
95. Problemy z portem
Błąd:
EADDRINUSE
oznacza, że port jest zajęty.
Linux:
ss -ltnp
lub:
sudo lsof -i :3000
96. Problem: command not found
Przykład:
vite: command not found
Najpierw:
npm install
lub:
npm ci
Nie musisz instalować Vite globalnie, jeśli jest zależnością projektu.
97. Problem: Cannot find module
Przykład:
Cannot find module 'express'
Najczęściej:
npm install
albo:
npm ci
98. Problem: wersja Node
Możesz zobaczyć błędy typu:
Unsupported engine
Sprawdź:
node -v
i:
"engines": {
"node": ">=22"
}
99. engines
W package.json:
"engines": {
"node": ">=22"
}
To informacja, jakiej wersji Node oczekuje projekt.
100. Problem z ES Modules
Błędy związane z:
require
import
module.exports
export
często wynikają z mieszania:
CommonJS
i:
ES Modules
Sprawdź:
"type": "module"
101. Problem z .env
Aplikacja działa lokalnie, ale nie na serwerze.
Najczęstsze przyczyny:
brak DATABASE_URL
brak PORT
brak API_KEY
brak JWT_SECRET
Sprawdź środowisko procesu.
Systemd:
systemctl show myapp --property=Environment
102. Debugowanie deploymentu
Kolejność:
systemctl status myapp
potem:
journalctl -u myapp -n 100
potem:
ss -ltnp
potem lokalnie:
curl http://127.0.0.1:3000
Jeżeli działa lokalnie, ale nie przez domenę:
sprawdź Nginx.
sudo nginx -t
Następnie:
sudo systemctl status nginx
103. curl - bardzo ważny przy backendzie
GET:
curl http://localhost:3000/api/users
POST:
curl \
-X POST \
-H "Content-Type: application/json" \
-d '{"name":"Anna"}' \
http://localhost:3000/api/users
104. Bezpieczeństwo
Nie commituj:
.env
API keys
hasła
tokeny
certyfikaty prywatne
Szukaj przypadkiem wrzuconych sekretów:
git grep -i "password"
lub:
git grep -i "api_key"
105. npm scripts mogą robić wszystko
To bardzo ważne.
Przykład:
"scripts": {
"build": "rm -rf dist && tsc",
"start": "node dist/server.js"
}
npm run build może wykonać praktycznie dowolne polecenie shellowe.
Dlatego przed uruchomieniem nieznanego projektu warto zobaczyć:
"scripts"
106. Lifecycle scripts
npm ma specjalne hooki:
preinstall
install
postinstall
prepare
prepublish
Przykład:
"scripts": {
"postinstall": "node scripts/setup.js"
}
Oznacza to, że:
npm install
może automatycznie uruchomić kod.
To ważne również ze względów bezpieczeństwa.
107. instalowanie bez scripts
Dla nieznanego projektu można czasami najpierw użyć:
npm install --ignore-scripts
albo:
npm ci --ignore-scripts
Pozwala obejrzeć projekt bez uruchamiania jego lifecycle scripts.
Nie zawsze aplikacja będzie potem kompletna, ale to przydatne przy analizie obcego repo.
108. package.json - szybki audyt
Przed uruchomieniem projektu sprawdź:
name
version
type
scripts
dependencies
devDependencies
engines
packageManager
109. Co jeszcze może znajdować się w projekcie
eslint.config.js
.prettierrc
vite.config.js
webpack.config.js
tsconfig.json
Dockerfile
docker-compose.yml
compose.yml
.env.example
prisma/
migrations/
.github/workflows/
Każdy z tych plików mówi coś ważnego o projekcie.
110. docker compose
Projekt może wymagać kilku usług:
Node
PostgreSQL
Redis
Przykład:
services:
app:
build: .
ports:
- "3000:3000"
db:
image: postgres:17
redis:
image: redis:7
Start:
docker compose up
W tle:
docker compose up -d
111. Bazy danych
Node sam nie jest bazą.
Popularne:
PostgreSQL
MySQL
MariaDB
SQLite
MongoDB
Redis
Sterownik PostgreSQL:
pg
Prisma może ukrywać część SQL za ORM.
112. Migracje
Projekt może posiadać:
migrations/
Są to kolejne zmiany schematu bazy.
Przykładowy workflow:
npm run migrate
lub:
npx prisma migrate deploy
Nie uruchamiaj migracji produkcyjnych bez sprawdzenia, co robią.
113. Prisma
Jeżeli widzisz:
prisma/schema.prisma
projekt używa Prisma.
Przykład:
model User {
id Int @id @default(autoincrement())
email String @unique
}
114. Middleware
Przykład:
app.use(authMiddleware);
Kod będzie wykonany przy requestach zanim request dotrze do endpointu.
Możliwe zastosowania:
auth
logging
CORS
rate limiting
JSON parsing
cookies
sessions
115. Callback
Starsza składnia asynchroniczna:
readFile("file.txt", (err, data) => {
if (err) {
console.error(err);
return;
}
console.log(data);
});
W nowszym kodzie częściej spotkasz:
await readFile(...)
116. Klasy
JavaScript posiada klasy:
class UserService {
constructor(repository) {
this.repository = repository;
}
async getUser(id) {
return this.repository.findById(id);
}
}
Spotkasz je szczególnie w:
- NestJS,
- większych aplikacjach enterprise.
117. this
this oznacza kontekst obiektu.
Przykład:
this.repository
zwykle oznacza:
pole
repositorybieżącego obiektu.
W JavaScript zachowanie this potrafi być bardziej skomplikowane niż w C++/Java, ale do czytania większości kodu ta definicja wystarcza.
118. Import aliasy
TypeScript może mieć:
import config from "@/config";
zamiast:
import config from "../../config";
Alias jest zazwyczaj zdefiniowany w:
tsconfig.json
vite.config.js
119. index.js jako barrel
Możesz zobaczyć:
export * from "./user.js";
export * from "./auth.js";
Plik index.js zbiera eksporty z wielu modułów.
120. Czytanie funkcji
Przykład:
async function createUser(data) {
const existing = await repository.findByEmail(data.email);
if (existing) {
throw new Error("User already exists");
}
return repository.create(data);
}
Czytaj krokami:
1. funkcja jest async
2. przyjmuje data
3. szuka użytkownika po mailu
4. jeśli istnieje -> błąd
5. jeśli nie -> tworzy użytkownika
Nie analizuj od razu każdego operatora.
Najpierw zrozum przepływ.
121. Jak czytać większą funkcję
Patrz na:
input
walidację
operacje
wywołania innych funkcji
return
błędy
Przykład:
request
|
v
validate
|
v
database lookup
|
v
business logic
|
v
database write
|
v
response
122. Jak sprawdzić, skąd pochodzi funkcja
Masz:
await sendEmail(user.email);
Najpierw znajdź import:
import { sendEmail } from "./mail.js";
Następnie otwórz:
mail.js
To podstawowa technika czytania projektu.
123. Jak znaleźć użycie funkcji
Shell:
grep -R "sendEmail" src/
Lepsze:
rg "sendEmail"
rg = ripgrep.
Bardzo dobre narzędzie do czytania kodu.
124. rg - podstawy
Szukaj tekstu:
rg "DATABASE_URL"
Szukaj funkcji:
rg "createUser"
Tylko JS:
rg "createUser" -g '*.js'
Tylko TS:
rg "createUser" -g '*.ts'
125. find
Znajdź package.json:
find . -name package.json
Znajdź pliki TypeScript:
find src -name '*.ts'
126. tree
Świetne do orientacji.
tree -L 2
Przykład:
.
├── package.json
├── src
│ ├── controllers
│ ├── routes
│ └── services
└── tests
127. npm ls
Lista paczek:
npm ls --depth=0
Pokazuje główne zależności.
128. npm explain
Chcesz wiedzieć, dlaczego paczka jest zainstalowana:
npm explain lodash
129. npm view
Informacje o paczce:
npm view express
Wersja:
npm view express version
130. Globalne paczki
Lista:
npm list -g --depth=0
Instalacja:
npm install -g some-tool
Nie instaluj globalnie bibliotek projektu takich jak:
express
react
axios
131. shebang w narzędziach CLI
Możesz zobaczyć:
#!/usr/bin/env node
Na początku pliku.
Oznacza:
uruchom ten skrypt przez Node.js.
132. CLI w Node
Przykład:
#!/usr/bin/env node
console.log(process.argv);
Uruchomienie:
node cli.js hello
133. Streams
Node bardzo często używa strumieni.
Przykład:
readStream.pipe(writeStream);
Mentalny model:
duży plik
|
v
małe kawałki danych
|
v
odbiorca
Dzięki temu nie trzeba ładować całego pliku do RAM.
134. Buffer
Node reprezentuje dane binarne przez:
Buffer
Przykład:
const buffer = Buffer.from("hello");
Spotkasz przy:
- plikach,
- sieci,
- obrazach,
- protokołach binarnych.
135. EventEmitter
Node ma system zdarzeń.
emitter.on("data", data => {
console.log(data);
});
Możesz myśleć o tym tak:
kiedy pojawi się event "data"
uruchom tę funkcję
136. HTTP bez frameworka
Node ma własny serwer HTTP.
import http from "node:http";
const server = http.createServer((req, res) => {
res.end("hello");
});
server.listen(3000);
Express i Fastify budują wygodniejsze API na tej podstawie.
137. fetch
W nowoczesnym Node można używać:
const response = await fetch("https://example.com");
Następnie:
const data = await response.json();
138. API REST
Typowe endpointy:
GET /users
GET /users/123
POST /users
PUT /users/123
PATCH /users/123
DELETE /users/123
Node bardzo często służy właśnie do takich API.
139. WebSocket
Node nadaje się również do:
- czatów,
- statusów live,
- gier,
- dashboardów,
- powiadomień.
Biblioteki:
ws
Socket.IO
140. worker_threads
Ciężkie operacje CPU można przenieść poza główny wątek.
worker_threads
Nie musisz znać szczegółów, ale gdy widzisz:
new Worker(...)
oznacza to wykorzystanie dodatkowego wątku.
141. child_process
Node może uruchamiać programy systemowe.
Przykład:
import { exec } from "node:child_process";
To potężne, ale wymaga ostrożności ze względów bezpieczeństwa.
Szczególnie jeżeli argument pochodzi od użytkownika.
142. Najważniejsze pułapki bezpieczeństwa
Zwracaj uwagę na:
eval(...)
exec(userInput)
fs.readFile(userControlledPath)
res.send(rawUserHtml)
oraz sekrety w kodzie.
143. Sanitizacja i walidacja
Backend nie powinien ufać danym klienta.
Przykładowe biblioteki:
Zod
Joi
Yup
Ajv
Przykład Zod:
const UserSchema = z.object({
email: z.string().email()
});
144. CORS
CORS kontroluje, jakie strony mogą wykonywać requesty do backendu.
Przykład:
app.use(cors());
Na produkcji często należy ograniczyć do konkretnego origin.
145. JWT
Token często używany do autoryzacji.
header.payload.signature
JWT nie jest szyfrowanym magazynem danych.
Dane tokenu można zwykle odczytać.
Bezpieczeństwo daje podpis.
146. Session
Alternatywa:
cookie
|
v
session ID
|
v
server
Node może przechowywać sesje np. w Redis.
147. Hashowanie haseł
Nigdy nie zapisuj hasła jako plain text.
Biblioteki:
bcrypt
argon2
148. Semantyczne wersjonowanie
Format:
MAJOR.MINOR.PATCH
Przykład:
4.2.7
- MAJOR - zmiana niekompatybilna,
- MINOR - nowe funkcje,
- PATCH - poprawki.
149. ESM rozszerzenia
Możesz zobaczyć:
.mjs
ES Module.
.cjs
CommonJS.
.js
znaczenie zależy między innymi od package.json.
150. npm cache
Sprawdzenie:
npm cache verify
Czyszczenie cache jest rzadko potrzebne.
Nie zaczynaj debugowania od:
npm cache clean --force
151. Memory leak i RAM
Sprawdzenie procesu:
ps aux | grep node
lub:
top
Na produkcji można obserwować:
RSS
heap
CPU
event loop lag
152. NODE_OPTIONS
Można przekazać ustawienia runtime.
Przykład:
NODE_OPTIONS="--max-old-space-size=4096" npm run build
Czasami buildy frontendowe potrzebują więcej RAM.
153. Exit code
Unix:
0 = sukces
inne = błąd
Node:
process.exit(0);
lub:
process.exit(1);
Ma znaczenie w:
- CI,
- systemd,
- Dockerze,
- skryptach shellowych.
154. Signals
Proces może dostać sygnał.
Typowe:
SIGTERM
SIGINT
Poprawna aplikacja powinna zamknąć się elegancko.
Przykład:
process.on("SIGTERM", async () => {
await server.close();
process.exit(0);
});
155. Graceful shutdown
Przy deploymencie proces może otrzymać SIGTERM.
Dobrze napisany serwer:
przestaje przyjmować requesty
czeka na bieżące
zamyka bazę
zamyka serwer
kończy proces
156. package-lock i bezpieczeństwo repo
Po aktualizacji zależności commitujesz zazwyczaj:
package.json
package-lock.json
Nie tylko package.json.
157. Monorepo
Projekt może mieć:
apps/
packages/
oraz narzędzia:
npm workspaces
pnpm workspaces
Turborepo
Nx
Wtedy jeden Git repozytorium zawiera wiele aplikacji/paczek.
158. Workspaces
Przykład:
{
"workspaces": [
"apps/*",
"packages/*"
]
}
Może istnieć:
apps/frontend
apps/backend
packages/shared
159. npm run z katalogu głównego
Monorepo często posiada skrypty typu:
npm run build
które budują wiele podprojektów.
Nie zakładaj, że package.json aplikacji znajduje się tylko jeden.
Sprawdź:
find . -name package.json -not -path '*/node_modules/*'
160. Source maps
Kod produkcyjny może być zbudowany.
Błąd pokazuje:
dist/server.js
ale źródła są w:
src/server.ts
Source maps pozwalają debuggerowi mapować:
dist -> src
Pliki:
*.map
161. Co oznacza dist
Najczęściej:
distribution
czyli gotowy output builda.
Nie edytuj ręcznie plików dist, jeżeli są generowane.
Edytuj src.
162. public
Katalog:
public/
zwykle zawiera:
- obrazy,
- favicon,
- statyczne pliki,
- czasami gotowy HTML.
163. static
Express może wystawiać statyczne pliki:
app.use(express.static("public"));
164. Template engines
Nie każdy Node backend zwraca tylko JSON.
Może generować HTML.
Popularne:
EJS
Pug
Handlebars
Nunjucks
Przykład:
res.render("index", { user });
165. Next.js
Next.js łączy:
- React,
- routing,
- server rendering,
- API/server functions,
- build tooling.
Typowy:
npm run dev
npm run build
npm start
Deploy bywa bardziej złożony niż zwykłego statycznego Reacta.
166. SSR
Server Side Rendering:
request
|
v
Node
|
v
generuje HTML
|
v
browser
Node musi działać na produkcji.
167. SPA
Single Page Application:
browser
|
v
HTML + JS
|
v
API
Frontend może być statyczny, backend osobno.
168. Jak ocenić, czy Node jest wymagany na produkcji
Po buildzie sprawdź:
Czy wynik to tylko:
index.html
assets/
*.js
*.css
Jeśli tak, prawdopodobnie można użyć samego Nginx.
Jeżeli start wymaga:
node server.js
Node musi działać na produkcji.
169. Kolejność analizy obcego repo
Praktyczna procedura:
1. README.md
2. package.json
3. lockfile
4. .env.example
5. Dockerfile / compose
6. tsconfig
7. główny entry point
8. routes
9. services
10. DB layer
11. tests
12. CI workflow
170. Pierwsze polecenia po clone
git clone ...
cd project
tree -L 2
cat package.json
cat README.md
ls -la
Potem:
node -v
npm -v
Następnie:
npm ci
i:
npm run
171. Audyt bez uruchamiania kodu
Dla obcego projektu:
cat package.json
Sprawdź:
scripts
postinstall
dependencies
Następnie:
find . -maxdepth 2 -type f | sort
i:
rg "process.env"
Pozwoli znaleźć wymagane zmienne środowiskowe.
172. Znajdowanie zmiennych środowiskowych
rg "process\.env"
Przykład wyniku:
process.env.PORT
process.env.DATABASE_URL
process.env.JWT_SECRET
Na tej podstawie można stworzyć:
.env
173. Znajdowanie portu
rg "listen\("
lub:
rg "PORT"
174. Znajdowanie endpointów
Express:
rg "router\.(get|post|put|patch|delete)"
lub:
rg "app\.(get|post|put|patch|delete)"
175. Znajdowanie bazy danych
rg "DATABASE_URL"
lub sprawdź zależności:
pg
mysql2
mongoose
prisma
sequelize
drizzle
176. Znajdowanie API zewnętrznych
rg "fetch\("
rg "axios"
rg "https://"
177. Czytanie stacku technologicznego z package.json
Przykład:
{
"dependencies": {
"express": "...",
"pg": "...",
"zod": "...",
"jsonwebtoken": "..."
},
"devDependencies": {
"typescript": "...",
"vitest": "...",
"eslint": "..."
}
}
Można od razu wywnioskować:
Express -> HTTP backend
pg -> PostgreSQL
Zod -> walidacja
JWT -> autoryzacja
TypeScript -> kod TS
Vitest -> testy
ESLint -> lintowanie
178. Jak nie czytać Node
Nie zaczynaj od:
node_modules/
To kod zależności.
Nie czytaj dist/, jeżeli jest generowany.
Nie analizuj minifikowanych plików.
Skup się na:
src/
app/
server/
packages/
179. Najważniejsze rozszerzenia plików
.js JavaScript
.mjs ES Module
.cjs CommonJS
.ts TypeScript
.tsx TypeScript + JSX
.jsx JavaScript + JSX
.json dane / konfiguracja
.yaml konfiguracja
.yml konfiguracja
.env zmienne środowiskowe
180. JSX
React używa składni:
function Button() {
return <button>Hello</button>;
}
To wygląda jak HTML, ale jest składnią JavaScript/JSX.
Node/Vite/React build przetwarza ją do JS.
181. import CSS
Frontend może mieć:
import "./style.css";
To nie jest standardowy import Node.
Obsługuje go bundler:
Vite
Webpack
Next.js
182. Dynamic import
const module = await import("./module.js");
Moduł jest ładowany dynamicznie.
Spotykane między innymi przy:
- lazy loading,
- pluginach,
- warunkowym ładowaniu modułów.
183. JSON import
Możesz spotkać:
import data from "./data.json" with { type: "json" };
lub starsze rozwiązania zależne od toolchainu.
184. top-level await
W ES Modules można czasem zobaczyć:
const data = await loadConfig();
bez opakowania w:
async function main()
185. main pattern
Częsty czytelny wzorzec:
async function main() {
// start aplikacji
}
main().catch(error => {
console.error(error);
process.exit(1);
});
To dobry punkt wejścia do analizy.
186. Dependency injection
W większych projektach zobaczysz konstrukcje typu:
new UserService(userRepository)
Oznacza:
UserService dostaje zależność z zewnątrz.
NestJS robi to bardzo intensywnie automatycznie.
187. Repository
Repository to warstwa dostępu do danych.
Przykład:
userRepository.findById(id)
Mentalny model:
service
|
v
repository
|
v
database
188. Controller
Controller zwykle tłumaczy:
HTTP request
na wywołanie logiki aplikacji.
I wynik:
service
na:
HTTP response
189. Service
Service powinien zawierać główną logikę biznesową.
Jeżeli chcesz zrozumieć:
co aplikacja naprawdę robi,
często warto czytać właśnie katalog:
services/
190. Config
Szukaj:
config/
configuration/
settings/
Znajdziesz tam:
- port,
- DB,
- API URL,
- feature flags,
- tryb produkcyjny.
191. Feature flags
Przykład:
if (config.enableNewCheckout) {
// ...
}
Pozwalają włączać/wyłączać funkcje.
192. Logging requestów
Popularne:
morgan
pino-http
Log:
GET /users 200 12ms
Pomaga debugować API.
193. Health endpoint
Dobra aplikacja może mieć:
GET /health
Przykład odpowiedzi:
{
"status": "ok"
}
Przydaje się do:
- Docker healthcheck,
- monitoringu,
- load balancera.
194. Healthcheck w shellu
curl -f http://127.0.0.1:3000/health
Exit code 0 oznacza sukces.
195. npm start produkcyjnie
Warto sprawdzić, czy:
npm start
naprawdę uruchamia produkcję.
Może być:
"start": "node dist/index.js"
ale czasami:
"start": "vite"
co niekoniecznie jest właściwe na produkcję.
Zawsze czytaj scripts.
196. Dev server to nie production server
Narzędzia takie jak:
Vite dev server
służą do developmentu.
Nie używaj:
npm run dev
jako produkcyjnego deploymentu bez powodu.
197. Minimalny workflow produkcyjny
git pull
npm ci
npm test
npm run build
sudo systemctl restart myapp
curl -f http://127.0.0.1:3000/health
Jeżeli wszystko przechodzi - deploy jest prawdopodobnie OK.
198. Rollback
Przed deploymentem warto znać poprzedni commit:
git log --oneline -5
Jeżeli deploy się nie uda:
git checkout <stary-commit>
npm ci
npm run build
sudo systemctl restart myapp
Lepsze systemy robią rollback automatycznie.
199. npm ci i production dependencies
Jeżeli aplikacja jest już zbudowana:
npm ci --omit=dev
instaluje tylko zależności produkcyjne.
Nie stosuj przed buildem, jeśli build potrzebuje:
typescript
vite
webpack
eslint
z devDependencies.
200. Build i deploy - dobre rozdzielenie
Model:
CI
|
+--> npm ci
+--> npm test
+--> npm run build
|
v
artefakt
|
v
serwer
Serwer produkcyjny dostaje gotowy artefakt.
To bardziej przewidywalne niż kompilowanie wszystkiego bezpośrednio na serwerze.
201. Node w Dockerze - dobry model mentalny
Docker image
├── Node runtime
├── aplikacja
├── node_modules
└── konfiguracja startu
Kontener nie jest maszyną wirtualną.
To izolowany proces z własnym filesystemem i zależnościami.
202. Docker volumes
Dane, które muszą przetrwać restart kontenera, nie powinny siedzieć tylko w filesystemie kontenera.
Dotyczy szczególnie:
uploadów
SQLite
plików użytkownika
Stosuje się:
volumes
203. Node i SQLite
Dla małych projektów SQLite może być świetne.
Plik:
app.db
Jeżeli aplikacja działa w Dockerze, ten plik powinien być na volume.
204. Node jako narzędzie developerskie
Nawet jeżeli aplikacja końcowa nie używa Node, Node może być potrzebny do:
npm install
npm run build
npm run lint
npm run test
To szczególnie ważne przy frontendzie.
205. Znaczenie lockfile podczas deploymentu
Bez lockfile:
npm install
dzisiaj może zainstalować trochę inne wersje niż za pół roku.
Z lockfile i:
npm ci
build jest znacznie bardziej przewidywalny.
206. Minimalna lista rzeczy, które trzeba znać ze składni
Jeżeli chcesz tylko czytać kod, opanuj rozpoznawanie:
const
let
object {}
array []
function
arrow function =>
if / else
for
return
async
await
try / catch
throw
import
export
class
new
this
?.
??
...
To pozwala zrozumieć zdecydowaną większość typowego kodu Node.
207. Mentalny model funkcji async
Kod:
const user = await getUser(id);
Czytaj:
wywołaj
getUser, poczekaj na wynik i przypisz go douser.
208. Mentalny model importu
Kod:
import { getUser } from "./userService.js";
Czytaj:
funkcja
getUserznajduje się wuserService.js.
209. Mentalny model middleware
Kod:
app.use(auth);
Czytaj:
zanim request pójdzie dalej, przejdzie przez
auth.
210. Mentalny model callbacku
Kod:
doSomething(result => {
console.log(result);
});
Czytaj:
kiedy
doSomethingbędzie miało wynik, uruchom tę funkcję.
211. Mentalny model map
users.map(user => user.name)
Czytaj:
dla każdego użytkownika pobierz jego
namei zbuduj z tego nową tablicę.
212. Mentalny model filter
users.filter(user => user.active)
Czytaj:
zostaw tylko aktywnych użytkowników.
213. Mentalny model destructuring
const { id, email } = user;
Czytaj:
wyciągnij pola
idiuser.
214. Mentalny model spread
const updated = {
...user,
active: false
};
Czytaj:
skopiuj user i zmień active na false.
215. Checklist: dostałem obce repo Node
[ ] Przeczytaj README.md
[ ] Otwórz package.json
[ ] Sprawdź scripts
[ ] Sprawdź Node engines
[ ] Sprawdź packageManager
[ ] Znajdź lockfile
[ ] Sprawdź .env.example
[ ] Znajdź entry point
[ ] Sprawdź Dockerfile / compose
[ ] Sprawdź testy
[ ] Sprawdź CI
[ ] npm ci
[ ] npm test
[ ] npm run build
[ ] npm start / npm run dev
216. Checklist: deployment na Debianie
[ ] Node właściwej wersji
[ ] osobny użytkownik systemowy
[ ] repo / artefakt w /opt
[ ] npm ci
[ ] npm test
[ ] npm run build
[ ] .env poza repo
[ ] systemd lub Docker
[ ] aplikacja na localhost
[ ] Nginx reverse proxy
[ ] HTTPS
[ ] logi
[ ] healthcheck
[ ] restart po awarii
[ ] autostart po reboot
217. Checklist: coś nie działa
1. node -v
2. npm -v
3. npm ci
4. npm run
5. npm test
6. npm run build
7. npm start
8. przeczytaj stack trace
9. sprawdź .env
10. sprawdź port
11. sprawdź logi
12. curl localhost
13. sprawdź Nginx
218. Najważniejsze komendy Node/npm
node -v
npm -v
npm install
npm ci
npm run
npm run dev
npm run build
npm test
npm start
npm outdated
npm audit
npm ls --depth=0
node --watch app.js
node --inspect app.js
node --test
219. Najważniejsze komendy deploymentowe
systemctl status myapp
systemctl restart myapp
journalctl -u myapp
journalctl -u myapp -f
ss -ltnp
curl http://127.0.0.1:3000
nginx -t
systemctl reload nginx
220. Najważniejsze pytania przy analizie projektu
Zadaj sobie:
1. Gdzie jest entry point?
2. Jak uruchamia się development?
3. Jak robi się build?
4. Co trafia do dist?
5. Jak uruchamia się production?
6. Jakiej wersji Node potrzebuje projekt?
7. Jakiego package managera używa?
8. Jakich zmiennych środowiskowych potrzebuje?
9. Jakiej bazy używa?
10. Czy wymaga migracji?
11. Jak uruchamia się testy?
12. Na jakim porcie działa?
13. Czy jest Dockerfile?
14. Czy jest system healthcheck?
15. Gdzie są logi?
221. Przykład analizy fikcyjnego projektu
Załóżmy:
promo-monitor/
├── package.json
├── package-lock.json
├── tsconfig.json
├── src/
│ ├── server.ts
│ ├── routes/
│ ├── services/
│ └── db/
├── tests/
├── .env.example
└── Dockerfile
package.json:
{
"type": "module",
"scripts": {
"dev": "tsx watch src/server.ts",
"build": "tsc",
"start": "node dist/server.js",
"test": "vitest"
},
"dependencies": {
"express": "...",
"pg": "...",
"zod": "..."
},
"devDependencies": {
"typescript": "...",
"tsx": "...",
"vitest": "..."
}
}
Z samego tego można wywnioskować:
- projekt jest w TypeScript,
- korzysta z ES Modules,
- development uruchamia tsx,
- produkcja działa z JavaScript z dist/,
- build wykonuje TypeScript compiler,
- backend używa Express,
- baza to PostgreSQL,
- Zod robi walidację,
- testy są w Vitest.
Workflow:
npm ci
npm test
npm run build
npm start
222. Przykład deploymentu tego projektu
cd /opt/promo-monitor
git pull
npm ci
npm test
npm run build
sudo systemctl restart promo-monitor
sudo systemctl status promo-monitor
Test:
curl -f http://127.0.0.1:3000/health
223. Node.js kontra Go - szybkie porównanie mentalne
Go:
kod
|
v
kompilator
|
v
jeden binarny program
Node:
kod JS
|
v
Node runtime
|
v
proces
TypeScript:
kod TS
|
v
tsc
|
v
JavaScript
|
v
Node
Frontend:
JS/TS/React
|
v
Vite
|
v
HTML/CSS/JS
|
v
Nginx / CDN
224. Node.js kontra JavaScript
JavaScript to język.
Node.js to środowisko uruchomieniowe JavaScript.
Tak samo jak:
C -> język
gcc -> kompilator
w przybliżeniu:
JavaScript -> język
Node.js -> runtime
225. Co musisz naprawdę zapamiętać
Jeżeli masz pamiętać tylko kilka rzeczy:
package.json mówi, jak projekt działa.
npm ci instaluje zależności powtarzalnie.
npm run pokazuje dostępne skrypty.
npm run build buduje projekt, jeżeli build istnieje.
npm start uruchamia aplikację, ale zawsze sprawdź, co faktycznie robi.
Node nie zawsze oznacza backend.
Frontend może potrzebować Node tylko do builda.
Na produkcji używaj systemd albo Dockera.
Nginx powinien stać przed backendem.
.env zawiera konfigurację i sekrety.
Nie commituj sekretów.
Czytaj kod od entry pointu, potem routes -> controller -> service -> database.
Testy są często najlepszą dokumentacją zachowania systemu.
Nie zaczynaj analizy od node_modules.
226. Ściąga - analiza projektu w 5 minut
tree -L 2
cat package.json
cat README.md
cat .env.example
npm run
rg "process\.env"
rg "listen\("
find . -name package.json -not -path '*/node_modules/*'
npm ls --depth=0
Po tych poleceniach zwykle wiesz już bardzo dużo.
227. Ściąga - czytanie składni
const x = 1;
stała.
let x = 1;
zmienna.
const x = {};
obiekt.
const x = [];
tablica.
const x = () => {};
funkcja.
await foo();
czekaj na wynik operacji async.
const { a, b } = object;
wyciągnij pola.
const x = { ...a };
skopiuj pola obiektu.
user?.email
bezpieczny dostęp.
value ?? defaultValue
wartość domyślna dla null/undefined.
import x from "module";
import.
export x;
eksport.
228. Ściąga - deployment
git pull
npm ci
npm test
npm run build
restart procesu
healthcheck
Systemd:
sudo systemctl restart myapp
sudo systemctl status myapp
journalctl -u myapp -f
Docker:
docker build -t myapp .
docker run -d --name myapp myapp
229. Co warto umieć później
Jeżeli będziesz chciał wejść poziom głębiej, warto poznać:
TypeScript
Express albo Fastify
REST
WebSocket
PostgreSQL
Redis
Docker
Nginx
systemd
GitHub Actions
Vitest
ESLint
Zod
Prisma lub Drizzle
Ale do czytania, uruchamiania i deployowania większości projektów Node nie musisz być ekspertem w żadnym z nich.
230. Ostateczny model mentalny
Node.js projekt można sprowadzić do kilku pytań:
Jaki kod?
↓
src/
Jakie zależności?
↓
package.json
Jakie dokładne wersje?
↓
package-lock.json
Jak uruchomić development?
↓
npm run dev
Jak zbudować?
↓
npm run build
Co jest wynikiem?
↓
dist/ albo build/
Jak uruchomić production?
↓
npm start / node dist/...
Jak skonfigurować?
↓
.env / process.env
Jak sprawdzić działanie?
↓
npm test / curl / healthcheck
Jak utrzymać proces?
↓
systemd / Docker
Jak wystawić internetowo?
↓
Nginx + HTTPS
Jeżeli potrafisz odpowiedzieć na te pytania, rozumiesz operacyjnie projekt Node.js, nawet jeżeli nie zamierzasz zawodowo pisać w nim kodu.
Oficjalne źródła
- Node.js documentation: https://nodejs.org/docs/latest/api/
- Node.js release schedule: https://nodejs.org/en/about/previous-releases
- npm documentation: https://docs.npmjs.com/