Fiszki Online Node.js (Preview)
Darmowy podgląd 15 z 45 dostępnych pytań
Podstawy Node.js
Czym jest Node.js i czym różni się od tradycyjnych serwerów WWW, takich jak Apache czy Nginx?
Node.js to środowisko wykonawcze dla języka JavaScript, które opiera się na silniku V8 (używanym również przez przeglądarkę Google Chrome). Pozwala ono na uruchamianie kodu JavaScript poza przeglądarką, co umożliwia tworzenie serwerowych aplikacji sieciowych. W odróżnieniu od tradycyjnych serwerów WWW, takich jak Apache czy Nginx, które wykorzystują model wielowątkowy i tworzą osobny wątek dla obsługi każdego zapytania, Node.js jest jednoprocesowy i używa asynchronicznego, opartego na zdarzeniach modelu I/O. Takie podejście umożliwia obsługę bardzo dużej liczby jednoczesnych połączeń przy relatywnie niskich nakładach pamięci i procesora.
- Więcej informacji o Node.js znajdziesz w oficjalnej dokumentacji: Node.js Documentation (polska) oraz Node.js Fundamentals (angielska).
Jak Node.js zarządza operacjami wejścia/wyjścia (I/O)?
Node.js zarządza operacjami wejścia/wyjścia (I/O) w sposób asynchroniczny i nieblokujący. Dzięki temu, operacje takie jak odczyt/zapis do pliku, operacje sieciowe czy zapytania do bazy danych wykonywane są w tle, bez blokowania głównego wątku wykonawczego. Kluczowym elementem umożliwiającym ten model jest event loop oraz biblioteka libuv, która abstrakuje operacje systemowe i zarządza kolejkowaniem zdarzeń.
- Dla szczegółowego omówienia mechanizmu asynchronicznego I/O, warto zapoznać się z artykułem: Event Loop, Timers, and process.nextTick() (angielski).
Opisz rolę oraz znaczenie jednoprocesowego pętli zdarzeń (event loop) w Node.js.
Pętla zdarzeń (event loop) jest centralnym mechanizmem zarządzania asynchronicznością w Node.js. W modelu jednoprocesowym, gdzie operacje I/O są wykonywane asynchronicznie, event loop pełni następujące role:
Obsługa zdarzeń: Kolejkuje zdarzenia (np. zakończenie operacji wejścia/wyjścia) i odpowiednio je dystrybuuje do callbacków.
Kolejkowanie zadań: Zarządza kolejkami różnych typów zadań, takich jak timer’y, operacje I/O czy zadania microtask.
Efektywność: Dzięki temu, że pętla zdarzeń działa w jednolitym wątku, eliminowane są koszty zarządzania wieloma wątkami, co daje możliwość obsługi tysięcy równoczesnych połączeń bez znaczącego spadku wydajności.
Aby lepiej zrozumieć działanie event loop, polecam zapoznanie się z wpisem: Jak działa Node.js? (polski) oraz z oficjalnymi materiałami: The Node.js Event Loop (angielski).
Programowanie Asynchroniczne i Pętla Zdarzeń w Node.js
Czym są Promise i w jaki sposób ułatwiają pracę dewelopera w porównaniu z tradycyjnymi callbackami?
Promise to obiekt reprezentujący wynik operacji asynchronicznej, który może przejść przez stany: pending (oczekiwanie), fulfilled (spełniony) lub rejected (odrzucony). Korzystanie z Promise znacznie poprawia czytelność kodu poprzez:
- Redukcję zagnieżdżenia (tzw. callback hell).
- Lepsze zarządzanie obsługą błędów (poprzez metodę
.catch()). - Możliwość łańcuchowania asynchronicznych operacji za pomocą
.then().
W porównaniu z tradycyjnymi callbackami, Promise pozwala stworzyć bardziej liniowy i łatwiejszy w utrzymaniu kod.
Źródła:
↑ Powrót na góręW jaki sposób async/await upraszcza kod asynchroniczny? Podaj przykład.
Słowa kluczowe async i await wprowadzają składnię, która umożliwia pisanie kodu asynchronicznego w sposób zbliżony do synchronicznego. Funkcje oznaczone jako async automatycznie zwracają Promise, a wyrażenie await powoduje "zatrzymanie" wykonania funkcji do momentu rozwiązania Promise. Dzięki temu kod staje się bardziej czytelny i zrozumiały, eliminując potrzebę łańcuchowania .then() oraz ułatwiając obsługę błędów za pomocą try/catch.
Przykład:
// JavaScript
async function pobierzDane() {
try {
const response = await fetch('https://api.przyklad.com/dane');
if (!response.ok) {
throw new Error('Błąd sieciowy');
}
const dane = await response.json();
return dane;
} catch (error) {
console.error('Wystąpił błąd:', error);
}
}
Źródła:
↑ Powrót na góręCo oznaczają terminy „callback hell” lub „piramida zagłady” i jak można ich uniknąć?
Callback hell, znany również jako piramida zagłady, odnosi się do sytuacji, gdy kod asynchroniczny jest zagnieżdżony w wielu warstwach callbacków. Efektem jest skomplikowana, trudna do odczytania i utrzymania struktura kodu, która podatna jest na błędy w obsłudze błędów i logikę programu.
Aby uniknąć callback hell, warto zastosować następujące techniki:
- Używanie Promise'ów, które umożliwiają łańcuchowanie wywołań.
- Korzystanie z async/await, co pozwala pisać kod asynchroniczny w sposób liniowy.
- Modularizacja funkcji i poprawne zarządzanie zależnościami między operacjami.
Źródła:
- Callback Hell – omówienie problemu (angielski)
- Artykuł o strukturze asynchronicznej w JavaScript (polski)
NPM oraz Zarządzanie Pakietami
Czym jest npm i dlaczego jest kluczowy dla rozwoju aplikacji Node.js?
npm (Node Package Manager) to narzędzie, które pełni rolę menadżera pakietów dla środowiska Node.js. Umożliwia łatwe pobieranie, zarządzanie oraz udostępnianie bibliotek i narzędzi, co znacząco usprawnia proces tworzenia i rozwijania aplikacji. Dzięki npm deweloperzy mogą korzystać z gotowych rozwiązań, co przyspiesza pracę nad projektem i pomaga utrzymać spójność kodu.
Linki referencyjne:
Jaka jest rola pliku package.json?
Plik package.json jest centralnym miejscem, gdzie zapisane są wszystkie metadane projektu Node.js. Zawiera on podstawowe informacje, takie jak:
- Nazwa i wersja projektu.
- Lista zależności (
dependencies) oraz zależności developerskich (devDependencies). - Skrypty (np.
start,test), które ułatwiają automatyzację zadań. - Konfiguracje dotyczące projektu i narzędzi, które mogą być wykorzystywane przez inne moduły.
Dzięki package.json możliwe jest łatwe odtworzenie środowiska projektowego poprzez npm install, co pobiera wszystkie niezbędne pakiety według zdefiniowanych wersji.
Linki referencyjne:
Na czym polega różnica między zależnościami (dependencies) a zależnościami developerskimi (devDependencies)?
- dependencies: Zawierają pakiety niezbędne do działania aplikacji w środowisku produkcyjnym. Są to moduły, które aplikacja wykorzystuje w trakcie wykonywania swoich funkcji użytkowych.
- devDependencies: Zawierają pakiety wykorzystywane wyłącznie w procesie tworzenia, testowania lub budowania aplikacji. Mogą to być narzędzia takie jak testery, transpilery, bundlery czy linters. Te zależności nie są instalowane w środowisku produkcyjnym, co pomaga zmniejszyć rozmiar i potencjalne wektory ataku na aplikację.
Podział ten umożliwia optymalizację instalacji oraz zarządzanie bezpieczeństwem i wydajnością aplikacji, izolując ścieżki działające w różnych środowiskach.
Linki referencyjne:
- Oficjalna dokumentacja npm – dependencies vs devDependencies
- Artykuł: "Gdzie umieszczać zależności?" (polski)
Express.js oraz Frameworki Webowe
Jak skonfigurować podstawową aplikację opartą na Express.js?
Aby stworzyć prostą aplikację Express, należy:
- Zainstalować Express poprzez npm:
npm install express
- Utworzyć plik (np.
app.js) i skonfigurować podstawową aplikację:
const express = require('express');
const app = express();
const port = 3000;
// Definiowanie prostej trasy GET
app.get('/', (req, res) => {
res.send('Witaj, Express!');
});
// Uruchomienie serwera
app.listen(port, () => {
console.log(`Serwer działa na porcie ${port}`);
});
- Uruchomić aplikację za pomocą:
node app.js
Materiały referencyjne:
↑ Powrót na góręCzym są funkcje middleware w Express i jak się je wykorzystuje?
Middleware w Express to funkcje, które mają dostęp do obiektu żądania (req), obiektu odpowiedzi (res) oraz kolejnej funkcji middleware w cyklu żądanie-odpowiedź. Służą one do:
- Przetwarzania żądań (np. parsowanie ciała żądania, uwierzytelnianie).
- Rejestrowania logów.
- Obsługi błędów.
Przykład użycia middleware:
const express = require('express');
const app = express();
// Prosty middleware rejestrujący każde żądanie
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next(); // Przekazanie kontroli dalej
});
app.get('/', (req, res) => {
res.send('Strona główna');
});
app.listen(3000, () => {
console.log('Serwer działa na porcie 3000');
});
Materiały referencyjne:
↑ Powrót na góręJaką rolę pełni middleware do obsługi błędów w aplikacji Express?
Middleware do obsługi błędów jest specjalnym rodzajem funkcji middleware, która wychwytuje błędy występujące podczas przetwarzania żądań. Główne cechy:
- Jego sygnatura zawiera cztery argumenty:
(err, req, res, next). - Po napotkaniu błędu, przekazujemy go do middleware obsługującego błędy, wywołując funkcję
next(err). - Umożliwia centralne zarządzanie błędami, co ułatwia logowanie i wysyłanie odpowiednich komunikatów do klienta, często bez ujawniania szczegółowych informacji o błędzie.
Przykład implementacji:
// Middleware obsługujący błędy
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Wystąpił błąd serwera!');
});
Materiały referencyjne:
- Dokumentacja Express na temat obsługi błędów (eng.)
- Artykuł o najlepszych praktykach w obsłudze błędów w Express (PL)
Bazy Danych i Zarządzanie Danymi
Jak połączyć aplikację Node.js z bazą danych (np. MongoDB, PostgreSQL)?
Aplikację Node.js możemy połączyć z bazą danych za pomocą dedykowanych sterowników lub bibliotek ODM/ORM. Dla MongoDB popularne są:
- MongoDB Native Driver – oficjalny sterownik, który daje pełną kontrolę nad operacjami.
- Mongoose – biblioteka ODM, która umożliwia definiowanie schematów oraz walidację danych.
Dla PostgreSQL możemy używać:
- node-postgres (pg) – oficjalny klient.
- Sequelize – ORM, który obsługuje różne bazy danych SQL, w tym PostgreSQL.
Przykład połączenia z MongoDB za pomocą Mongoose:
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/nazwa_bazy', {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => console.log('Połączono z bazą MongoDB'))
.catch(err => console.error('Błąd połączenia', err));
Przydatne materiały:
- Oficjalna dokumentacja Mongoose (ang.)
- node-postgres – dokumentacja (ang.)
- Poradnik łączenia Node.js z MongoDB (PL)
Jakie są najlepsze praktyki obsługi operacji asynchronicznych przy pracy z bazami danych w Node.js?
Obsługa operacji asynchronicznych w Node.js jest kluczowa ze względu na jego model zdarzeniowy:
- Async/Await – umożliwia czytelniejszy kod oraz lepsze zarządzanie błędami przy wykorzystaniu try/catch.
- Promises – pozwalają na łańcuchowe operacje asynchroniczne, redukując tzw. "callback hell".
- Obsługa błędów – stosowanie bloków try/catch w async/await lub metod
.catch()przy korzystaniu z Promise. - Pooling połączeń – korzystanie z pul połączeń, aby unikać wyczerpania zasobów.
Przykład z async/await:
async function getData() {
try {
const result = await Model.find({}); // przykład dla Mongoose
return result;
} catch (error) {
console.error('Błąd pobierania danych:', error);
throw error;
}
}
getData();
Przydatne materiały:
- Asynchroniczność w JavaScript – poradnik (PL)
- Best practices for asynchronous programming in Node.js (ang.)
W jaki sposób zabezpieczyć zapytania do bazy danych przed atakami typu injection?
Aby zabezpieczyć zapytania przed atakami SQL Injection lub NoSQL Injection, należy:
- Korzystać z parametrów zapytań: Używanie zapytań przygotowanych (prepared statements) lub mechanizmów wbudowanych w biblioteki.
- Walidacja i sanitizacja danych wejściowych: Weryfikacja danych użytkownika przed użyciem ich w zapytaniu.
- Używanie mechanizmów ORM/ODM: Większość bibliotek automatycznie zapobiega atakom injection dzięki wbudowanej obsłudze parametrów.
Przykład zabezpieczonego zapytania dla PostgreSQL przy użyciu node-postgres:
const { Pool } = require('pg');
const pool = new Pool();
async function getUserByEmail(email) {
const query = 'SELECT * FROM users WHERE email = $1';
const { rows } = await pool.query(query, [email]);
return rows;
}
Przydatne materiały:
- Bezpieczeństwo aplikacji Node.js – artykuł (PL)
- OWASP SQL Injection – przewodnik (ang.)
- MongoDB Injection – informacje (ang.)