Pytania rekrutacyjne Node
53 pytań — 20 odpowiedzi dostępnych od razu
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).
Jakie są kluczowe różnice między systemami modułów CommonJS a ES Modules w Node.js?
CommonJS
- Sposób importu/eksportu: Używa funkcji
require()do importowania modułów orazmodule.exportsdo ich eksportowania. - Synchronizacja: Import modułów odbywa się synchronicznie, co jest akceptowalne w środowisku serwerowym, gdzie moduły są ładowane przy starcie aplikacji.
- Historia: Jest to tradycyjny system modułów, używany od początków Node.js.
ES Modules (ESM)
Sposób importu/eksportu: Wykorzystuje składnię
importiexport, co jest standardem w nowoczesnym JavaScript.Asynchroniczność: Import modułów może być wykonywany asynchronicznie, co wpływa na sposób bootstrapowania aplikacji.
Modułowość: Umożliwia lepszą optymalizację przy wykonywaniu kodu, zwłaszcza w przypadku aplikacji webowych.
Konfiguracja: Wprowadzanie ESM do istniejących projektów może wymagać drobnych zmian w konfiguracji (np. określenie typu modułów w pliku
package.json).Więcej informacji na temat różnic można znaleźć w artykule: Modules: CommonJS vs ES Modules (angielski) oraz w polskojęzycznych opracowaniach, np. na blogu DevStyle - ESM vs CommonJS.
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).
Jak działają callbacki w Node.js i jakie ograniczenia lub pułapki mogą się z nimi wiązać?
Callbacki w Node.js są funkcjami przekazywanymi jako argumenty do innych funkcji, które mają zostać wykonane po zakończeniu operacji asynchronicznej. Działają one w następujący sposób:
- Mechanizm asynchroniczny: Po wykonaniu operacji (np. odczytu pliku czy zapytania sieciowego), wywoływany jest odpowiedni callback, który otrzymuje wyniki działania tej operacji.
- Error-first callback: Wiele funkcji Node.js przyjmuje jako pierwszy argument callbacków funkcję, która na początku przyjmuje obiekt błędu (jeśli wystąpił) oraz wynik operacji. Pomaga to w jednolitym zarządzaniu błędami.
Ograniczenia i pułapki:
Callback Hell: Głębokie zagnieżdżanie callbacków utrudnia czytelność i utrzymanie kodu. Rozwiązaniem tego problemu mogą być takie podejścia jak Promisy czy async/await.
Brak obsługi wyjątków: Błędy generowane wewnątrz callbacków mogą być trudniejsze do wykrycia, jeśli nie są odpowiednio obsłużone.
Kontext wykonania: Utrata kontekstu lub niezamierzone nadpisanie zmiennych lokalnych podczas zagnieżdżania może prowadzić do błędów logicznych.
Więcej na ten temat można przeczytać w artykule: Callback vs Promises: Jak przekształcić callback hell w cleaner code (polski) oraz w dyskusjach na StackOverflow.
Programowanie Asynchroniczne i Pętla Zdarzeń w Node.js
Na czym polega programowanie asynchroniczne w Node.js?
Programowanie asynchroniczne w Node.js polega na wykonywaniu operacji wejścia/wyjścia (I/O) oraz innych zadań, które mogą trwać pewien czas, bez blokowania głównego wątku wykonawczego. Dzięki temu serwer może obsługiwać wiele żądań jednocześnie, co zwiększa skalowalność i wydajność aplikacji. Node.js opiera się na modelu zdarzeniowym i używa mechanizmu o nazwie event loop, który rejestruje operacje asynchroniczne oraz wywołuje odpowiednie callbacki po ich zakończeniu.
Źródła:
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:
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:
Co to jest pętla zdarzeń (event loop) i jak zarządza operacjami asynchronicznymi?
Pętla zdarzeń (event loop) jest mechanizmem Node.js, który ciągle monitoruje stos wywołań oraz kolejkę zdarzeń, zapewniając, że funkcje callback powiązane z operacjami asynchronicznymi są wykonywane po zakończeniu odpowiednich operacji I/O. W uproszczeniu, event loop pobiera zadania z kolejki, wykonuje je jedno po drugim i ponownie sprawdza kolejkę, umożliwiając równoczesne przetwarzanie wielu operacji bez blokowania głównego wątku.
Źródła:
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:
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:
Jak zarządzać zależnościami w projekcie Node.js?
Zarządzanie zależnościami w projekcie Node.js odbywa się głównie za pomocą narzędzia npm i pliku package.json. Kluczowe kroki obejmują:
- Instalowanie pakietów za pomocą
npm install <nazwa-pakietu>, które umożliwia dodanie zależności. - Dodawanie zależności developerskich przy użyciu flagi
--save-dev, co rozróżnia pakiety potrzebne tylko podczas rozwoju. - Utrzymywanie wersji pakietów zgodnych z wymaganą specyfikacją przy pomocy semantycznego wersjonowania (semver).
- Regularne aktualizowanie zależności za pomocą
npm updatelub narzędzi takich jaknpm auditw celu identyfikacji i poprawy potencjalnych luk bezpieczeństwa.
Linki referencyjne: - Oficjalna dokumentacja npm – zarządzanie zależnościami
- Poradnik: "Zarządzanie zależnościami w Node.js" (polski)
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:
Jak postępować z aktualizacjami wersji zależności w środowisku produkcyjnym?
Aktualizacje zależności w środowisku produkcyjnym powinny być przeprowadzane ostrożnie, aby zapewnić stabilność aplikacji. Typowe podejście obejmuje:
- Używanie semantycznego wersjonowania (semver) w celu ścisłego określenia kompatybilności pakietów.
- Aktualizację zależności w środowisku testowym przed wdrożeniem na produkcję.
- Wykorzystywanie narzędzi takich jak
npm auditdo identyfikacji luk bezpieczeństwa oraznpm cido instalacji zależności zgodnie z plikiempackage-lock.json, co gwarantuje spójność instalacji. - Monitorowanie changelogów poszczególnych pakietów w celu zrozumienia wprowadzonych zmian i ewentualnych modyfikacji niezbędnych w kodzie.
Linki referencyjne: - Semantyczne wersjonowanie – Official Site
- npm audit – Dokumentacja
- Artykuł: "Bezpieczeństwo w Node.js – aktualizacja zależności" (polski)
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:
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:
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:
Jak zaprojektować strukturę aplikacji Express, aby była skalowalna i łatwa w utrzymaniu?
Przy projektowaniu skalowalnej aplikacji Express warto uporządkować strukturę projektu, dzieląc ją na moduły odpowiedzialne za różne aspekty aplikacji. Zalecane podejścia to:
- Podział na warstwy: rozdziel logikę biznesową, logikę HTTP (routing), dostęp do danych (model) oraz konfiguracje.
- Separacja routingu i logiki kontrolerów: Umieszczaj definicje tras w osobnych modułach lub folderach, a logikę obsługi żądań przenieś do kontrolerów.
- Modułowość: Korzystaj z modułów (CommonJS lub ES Modules), aby organizować kod w logiczne pakiety.
- Konfiguracja środowisk: Trzymaj konfigurację (np. port, ścieżki baz danych) poza kodem aplikacji, np. w plikach konfiguracyjnych (JSON, YAML, .env).
Przykładowa struktura projektu:
/project
/controllers
userController.js
/routes
userRoutes.js
index.js
/models
userModel.js
/middlewares
errorHandler.js
auth.js
app.js
package.json
Materiały referencyjne:
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:
W jaki sposób można implementować routing w projekcie Express oraz jakie kwestie warto uwzględnić przy projektowaniu zagnieżdżonych tras?
W Express routing można implementować na kilka sposobów:
- Podstawowy routing: Definiowanie tras bezpośrednio w głównym pliku aplikacji.
app.get('/users', (req, res) => {
res.send('Lista użytkowników');
});
Modułowy routing: Wydzielanie tras do osobnych plików przy użyciu obiektu
express.Router(). Pozwala to na organizację kodu oraz na tworzenie zagnieżdżonych tras.Przykład zagnieżdżonych tras:
const express = require('express');
const router = express.Router();
// Trasa główna dla użytkowników
router.get('/', (req, res) => {
res.send('Lista użytkowników');
});
// Zagnieżdżona trasa dla konkretnego użytkownika
router.get('/:userId', (req, res) => {
res.send(`Szczegóły użytkownika ${req.params.userId}`);
});
// Montowanie routera na ścieżce '/users'
app.use('/users', router);
Przy projektowaniu zagnieżdżonych tras warto uwzględnić:
- Spójność ścieżek: Upewnić się, że wszystkie trasy są logicznie pogrupowane.
- Obsługę parametrów: Walidację oraz sanitizację parametrów w trasach.
- Modularność: Ułatwienie rozwoju i utrzymania przez rozbicie kodu na mniejsze, niezależne moduły.
- Testowalność: Przygotowanie tras w taki sposób, aby można je było łatwo testować jednostkowo.
Materiały referencyjne:
Bazy Danych i Zarządzanie Danymi
Odpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziTestowanie i Debugowanie
Odpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOptymalizacja Wydajności oraz Skalowanie
Odpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziNajlepsze Praktyki Bezpieczeństwa
Odpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziTematy Zaawansowane i Specjalistyczne Obszary Wiedzy
Odpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 33 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedzi