Express.js - Pytania Rekrutacyjne dla Node.js Backend Developera [2026]
Rozmawiasz o stanowisko Node.js Backend Developer i wiesz, że Express.js to fundament większości aplikacji Node? Ten przewodnik zawiera 42 pytania rekrutacyjne z odpowiedziami - od podstaw po zaawansowane tematy jak middleware, routing i bezpieczeństwo.
Spis treści
Podstawy Express.js
Czym jest Express.js i dlaczego jest tak popularny?
Odpowiedź w 30 sekund: Express.js to minimalistyczny, elastyczny framework webowy dla Node.js. Dostarcza zestaw narzędzi do budowania aplikacji webowych i API bez narzucania konkretnej architektury. Jest popularny dzięki prostocie, rozbudowanemu ekosystemowi middleware i niskiej krzywej uczenia.
Odpowiedź w 2 minuty: Express.js jest de facto standardem dla budowania aplikacji webowych w Node.js. Jego popularność wynika z filozofii minimalnego core'a rozszerzanego przez middleware. Framework dostarcza podstawowe funkcjonalności routingu i obsługi HTTP, podczas gdy cała logika aplikacji (parsowanie body, uwierzytelnianie, logowanie) jest dodawana przez modułowe middleware.
Kluczowe cechy Express to:
- Prostota - API jest intuicyjne i łatwe do opanowania
- Elastyczność - brak narzuconej struktury projektu
- Middleware - rozbudowany ekosystem gotowych rozwiązań
- Wydajność - minimalne narzuty na obsługę żądań
Express jest bazą dla wielu popularnych frameworków jak NestJS, Sails.js czy LoopBack.
const express = require('express');
const app = express();
// Middleware
app.use(express.json());
// Route handler
app.get('/api/users', (req, res) => {
res.json({ users: [] });
});
// Uruchomienie serwera
app.listen(3000, () => {
console.log('Serwer działa na porcie 3000');
});
Jak stworzyć podstawową aplikację Express i co reprezentuje obiekt app?
Odpowiedź w 30 sekund:
Aplikację Express tworzymy wywołując funkcję express(), która zwraca obiekt app. Ten obiekt reprezentuje całą aplikację i zawiera metody do konfiguracji middleware (app.use), definiowania tras (app.get, app.post), ustawień (app.set) i uruchamiania serwera (app.listen).
Odpowiedź w 2 minuty:
Obiekt app w Express jest centralnym punktem aplikacji. To instancja klasy Express Application, która dziedziczy po Node.js EventEmitter. Zawiera wszystkie metody potrzebne do budowania aplikacji webowej.
Najważniejsze metody obiektu app:
| Metoda | Przeznaczenie |
|---|---|
app.use() |
Montowanie middleware |
app.get/post/put/delete() |
Definiowanie tras HTTP |
app.set() |
Ustawianie konfiguracji |
app.listen() |
Uruchamianie serwera |
app.route() |
Łańcuchowe definiowanie tras |
const express = require('express');
const app = express();
// Konfiguracja
app.set('view engine', 'ejs');
app.set('trust proxy', 1);
// Middleware
app.use(express.json());
app.use(express.static('public'));
// Trasy
app.get('/', (req, res) => {
res.send('Hello World');
});
// Nasłuchiwanie
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Serwer nasłuchuje na porcie ${PORT}`);
});
Routing
Jak definiować trasy w Express i jakie metody HTTP są dostępne?
Odpowiedź w 30 sekund:
Trasy definiuje się metodami odpowiadającymi metodom HTTP: app.get(), app.post(), app.put(), app.patch(), app.delete(). Każda metoda przyjmuje ścieżkę i funkcję callback (handler). Express obsługuje też app.all() dla wszystkich metod i app.route() dla łańcuchowego definiowania.
Odpowiedź w 2 minuty: Express routing to proces dopasowywania żądań HTTP do odpowiednich handlerów na podstawie ścieżki URL i metody HTTP.
// Podstawowe metody HTTP
app.get('/users', (req, res) => { /* pobierz */ });
app.post('/users', (req, res) => { /* utwórz */ });
app.put('/users/:id', (req, res) => { /* aktualizuj całość */ });
app.patch('/users/:id', (req, res) => { /* aktualizuj częściowo */ });
app.delete('/users/:id', (req, res) => { /* usuń */ });
// Wszystkie metody
app.all('/api/*', (req, res, next) => {
console.log('Żądanie API:', req.method);
next();
});
// Łańcuchowe definiowanie (app.route)
app.route('/articles')
.get((req, res) => res.json({ articles: [] }))
.post((req, res) => res.status(201).json({ created: true }));
Jak działają parametry trasy (route parameters) w Express?
Odpowiedź w 30 sekund:
Parametry trasy to dynamiczne segmenty URL oznaczone dwukropkiem (np. /users/:id). Są automatycznie dostępne w obiekcie req.params. Można używać wielu parametrów, opcjonalnych parametrów (?) oraz regex do walidacji formatu.
Odpowiedź w 2 minuty:
Route parameters pozwalają na tworzenie dynamicznych ścieżek. Express parsuje URL i udostępnia wartości w req.params jako obiekt klucz-wartość.
// Pojedynczy parametr
app.get('/users/:id', (req, res) => {
const userId = req.params.id;
res.json({ userId });
});
// Wiele parametrów
app.get('/users/:userId/posts/:postId', (req, res) => {
const { userId, postId } = req.params;
res.json({ userId, postId });
});
// Opcjonalny parametr
app.get('/users/:id/posts/:postId?', (req, res) => {
const { id, postId } = req.params;
if (postId) {
res.json({ message: `Post ${postId}` });
} else {
res.json({ message: 'Wszystkie posty' });
}
});
// Walidacja regex - tylko liczby
app.get('/products/:id(\\d+)', (req, res) => {
// Pasuje: /products/123
// Nie pasuje: /products/abc
res.json({ productId: req.params.id });
});
Czym jest express.Router() i jak organizować trasy w modułach?
Odpowiedź w 30 sekund:
express.Router() to mini-aplikacja Express służąca do tworzenia modularnych, montowanych grup tras. Pozwala na organizowanie tras w osobnych plikach według funkcjonalności (np. users, products), a następnie montowanie ich w głównej aplikacji używając app.use().
Odpowiedź w 2 minuty: Router działa jak mini-aplikacja - ma własne middleware i trasy, ale nie może działać samodzielnie. Musi być zamontowany w głównej aplikacji.
// routes/users.js
const express = require('express');
const router = express.Router();
// Middleware specyficzne dla routera
router.use((req, res, next) => {
console.log('Users Router - Time:', Date.now());
next();
});
router.get('/', (req, res) => {
res.json({ users: [] });
});
router.get('/:id', (req, res) => {
res.json({ user: { id: req.params.id } });
});
router.post('/', (req, res) => {
res.status(201).json({ message: 'Utworzono' });
});
module.exports = router;
// app.js
const express = require('express');
const usersRouter = require('./routes/users');
const productsRouter = require('./routes/products');
const app = express();
app.use('/api/users', usersRouter);
app.use('/api/products', productsRouter);
Struktura katalogów:
project/
├── app.js
├── routes/
│ ├── users.js
│ ├── products.js
│ └── orders.js
Jak działa kolejność definiowania tras i dlaczego jest ważna?
Odpowiedź w 30 sekund:
Express przetwarza trasy w kolejności ich definiowania (od góry do dołu) i wykonuje pierwszą pasującą trasę. Kolejność jest krytyczna - bardziej szczegółowe trasy muszą być przed ogólniejszymi, np. /users/admin przed /users/:id.
Odpowiedź w 2 minuty: Express używa algorytmu "first-match" - iteruje przez trasy i zatrzymuje się na pierwszej pasującej.
// ❌ ŹLE - /users/admin nigdy nie zostanie osiągnięte
app.get('/users/:id', (req, res) => {
res.json({ id: req.params.id });
});
app.get('/users/admin', (req, res) => {
res.json({ message: 'Panel admina' });
});
// ✅ DOBRZE - specyficzna trasa pierwsza
app.get('/users/admin', (req, res) => {
res.json({ message: 'Panel admina' });
});
app.get('/users/:id', (req, res) => {
res.json({ id: req.params.id });
});
Zasady kolejności:
- Statyczne ścieżki przed parametrycznymi
- Bardziej szczegółowe przed ogólnymi
- Middleware przed trasami
- Catch-all i 404 na końcu
Middleware
Czym jest middleware w Express.js i jak działa łańcuch middleware?
Odpowiedź w 30 sekund:
Middleware to funkcje mające dostęp do obiektów req, res i funkcji next() w cyklu żądanie-odpowiedź. Łańcuch middleware działa sekwencyjnie - każda funkcja może przetworzyć żądanie, zmodyfikować obiekty req/res, zakończyć żądanie lub przekazać kontrolę wywołując next().
Odpowiedź w 2 minuty: Middleware to fundamentalny wzorzec w Express. Żądanie przechodzi przez kolejne funkcje middleware jak przez pipeline.
// Middleware logowania
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
req.requestTime = Date.now();
next(); // Przekaż kontrolę dalej
});
// Middleware uwierzytelniania
app.use((req, res, next) => {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ error: 'Brak autoryzacji' });
// Brak next() - żądanie zakończone
}
next();
});
// Route handler
app.get('/api/data', (req, res) => {
res.json({
message: 'Dane',
requestTime: req.requestTime
});
});
Przepływ:
Request → Middleware 1 → Middleware 2 → Route Handler → Response
Jeśli middleware nie wywoła next() ani nie wyśle odpowiedzi, żądanie zostanie "zawieszone".
Jaka jest różnica między middleware aplikacji, routera i obsługi błędów?
Odpowiedź w 30 sekund:
Middleware aplikacji (app.use) działa globalnie, middleware routera (router.use) tylko dla tras w routerze, a middleware obsługi błędów ma cztery parametry (err, req, res, next) i obsługuje błędy wyrzucone przez poprzednie middleware.
Odpowiedź w 2 minuty:
Express oferuje trzy rodzaje middleware, które różnią się zakresem działania. Middleware aplikacji działa dla wszystkich żądań, middleware routera tylko dla tras w danym routerze, a middleware obsługi błędów jest specjalnym typem z czterema parametrami, który przechwytuje błędy z całego łańcucha.
// === MIDDLEWARE APLIKACJI ===
app.use(express.json()); // Globalne
app.use('/admin', (req, res, next) => {
console.log('Tylko /admin/*');
next();
});
// === MIDDLEWARE ROUTERA ===
const apiRouter = express.Router();
apiRouter.use((req, res, next) => {
req.apiVersion = '1.0';
next();
});
apiRouter.get('/users', (req, res) => {
res.json({ version: req.apiVersion });
});
app.use('/api', apiRouter);
// === MIDDLEWARE OBSŁUGI BŁĘDÓW ===
// Zawsze 4 parametry, zawsze na końcu
app.use((err, req, res, next) => {
console.error('Błąd:', err.message);
res.status(err.status || 500).json({
error: err.message
});
});
Jak działa funkcja next() i kiedy należy ją wywoływać?
Odpowiedź w 30 sekund:
next() przekazuje kontrolę do następnego middleware. Wywołanie next(err) z argumentem przeskakuje do middleware obsługi błędów. next('route') pomija pozostałe handlery i przechodzi do następnej pasującej trasy.
Odpowiedź w 2 minuty:
Funkcja next() to mechanizm kontroli przepływu w łańcuchu middleware. Bez jej wywołania żądanie zostaje "zawieszone" i nigdy nie otrzyma odpowiedzi. Jej różne warianty pozwalają na normalne przekazywanie kontroli, przeskakiwanie do error handlera lub pomijanie pozostałych handlerów trasy.
// Podstawowe next()
app.use((req, res, next) => {
req.timestamp = Date.now();
next(); // Kontynuuj do następnego
});
// next() z błędem
app.use((req, res, next) => {
const apiKey = req.headers['x-api-key'];
if (!apiKey) {
const error = new Error('Brak klucza API');
error.status = 401;
return next(error); // Przeskocz do error handler
}
next();
});
// next('route')
app.get('/user/:id', (req, res, next) => {
if (req.params.id === '0') {
return next('route'); // Pomiń, szukaj następnej trasy
}
res.json({ id: req.params.id });
});
app.get('/user/:id', (req, res) => {
res.json({ id: 0, name: 'Guest' });
});
// Asynchroniczne middleware
app.use(async (req, res, next) => {
try {
const user = await fetchUser(req.headers.userId);
req.user = user;
next();
} catch (error) {
next(error); // Przekaż błąd
}
});
Ważne: Nie wywołuj next() po wysłaniu odpowiedzi!
Czym jest body-parser i jak parsować dane JSON oraz formularzy?
Odpowiedź w 30 sekund:
Body-parser to middleware do parsowania body żądań HTTP. Od Express 4.16+ jest wbudowany jako express.json() dla JSON i express.urlencoded() dla formularzy. Parsuje dane i udostępnia je w req.body.
Odpowiedź w 2 minuty:
Od Express 4.16+ body-parser jest wbudowany w framework jako metody express.json() i express.urlencoded(). Te middleware parsują treść żądania HTTP i udostępniają ją w obiekcie req.body. Bez nich req.body jest undefined, co jest częstym błędem początkujących.
// Parsowanie JSON
app.use(express.json({
limit: '10mb', // Max rozmiar (domyślnie 100kb)
strict: true // Tylko tablice i obiekty
}));
// Parsowanie formularzy HTML
app.use(express.urlencoded({
extended: true, // Zagnieżdżone obiekty
limit: '10mb'
}));
// Użycie
app.post('/api/users', (req, res) => {
const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({
error: 'Name i email są wymagane'
});
}
res.status(201).json({
message: 'Użytkownik utworzony',
user: { name, email }
});
});
Bez parsera req.body jest undefined.
Request i Response
Jak obsługiwać parametry zapytania (query parameters)?
Odpowiedź w 30 sekund:
Query parameters to pary klucz-wartość po ? w URL (np. /api/users?sort=name&limit=10). Express parsuje je automatycznie i udostępnia w req.query. Nie wymagają konfiguracji w definicji trasy.
Odpowiedź w 2 minuty:
Query parameters to najpopularniejszy sposób przekazywania opcjonalnych danych filtrowania, sortowania i paginacji do API. Express automatycznie parsuje je z URL i udostępnia jako obiekt w req.query, gdzie wszystkie wartości są stringami i wymagają konwersji typów.
// URL: /api/products?category=electronics&minPrice=100&sort=price-asc
app.get('/api/products', (req, res) => {
const { category, minPrice, maxPrice, sort } = req.query;
let products = getAllProducts();
// Filtrowanie
if (category) {
products = products.filter(p => p.category === category);
}
if (minPrice) {
products = products.filter(p => p.price >= parseFloat(minPrice));
}
// Sortowanie
if (sort === 'price-asc') {
products.sort((a, b) => a.price - b.price);
}
res.json({ count: products.length, products });
});
// Paginacja
app.get('/api/posts', (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const startIndex = (page - 1) * limit;
const paginatedPosts = posts.slice(startIndex, startIndex + limit);
res.json({
page,
limit,
total: posts.length,
totalPages: Math.ceil(posts.length / limit),
data: paginatedPosts
});
});
Jak prawidłowo wysyłać odpowiedzi JSON w Express?
Odpowiedź w 30 sekund:
Używaj res.json(), która automatycznie konwertuje obiekty na JSON i ustawia Content-Type. Dla błędów używaj kodów statusu: res.status(400).json({ error: 'message' }). Zawsze wysyłaj tylko jedną odpowiedź.
Odpowiedź w 2 minuty:
Metoda res.json() to preferowany sposób wysyłania danych w API RESTful. Automatycznie serializuje obiekty JavaScript do JSON i ustawia odpowiedni Content-Type. Kluczowe jest używanie prawidłowych kodów statusu HTTP i spójnego formatu odpowiedzi w całym API.
// Podstawowe wysyłanie JSON
app.get('/api/users', (req, res) => {
res.json({ users: [] });
});
// Standardowy format odpowiedzi
app.get('/api/users/:id', (req, res) => {
const user = findUser(req.params.id);
if (!user) {
return res.status(404).json({
success: false,
error: { message: 'Użytkownik nie znaleziony' }
});
}
res.json({
success: true,
data: user
});
});
// Tworzenie zasobu
app.post('/api/users', (req, res) => {
const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({
success: false,
error: { message: 'Brakujące pola' }
});
}
const newUser = createUser({ name, email });
res.status(201).json({
success: true,
data: newUser,
message: 'Użytkownik utworzony'
});
});
Ważne: Używaj return przed res.json() w warunkach, aby zatrzymać wykonanie.
Obsługa błędów
Jak prawidłowo obsługiwać błędy w Express.js?
Odpowiedź w 30 sekund:
Błędy obsługuje się przez middleware z 4 parametrami (err, req, res, next). Jest wywoływany gdy poprzedni middleware wyrzuci błąd lub przekaże go przez next(err). Musi być zdefiniowany jako ostatni.
Odpowiedź w 2 minuty:
Centralna obsługa błędów w Express opiera się na middleware z czterema parametrami. Ten specjalny typ middleware jest wywoływany gdy poprzednie middleware lub handler przekaże błąd przez next(err) lub gdy błąd zostanie rzucony synchronicznie. Musi być zdefiniowany jako ostatni w łańcuchu.
// Middleware mogące wyrzucić błąd
app.get('/api/users/:id', async (req, res, next) => {
try {
const user = await User.findById(req.params.id);
if (!user) {
const error = new Error('Użytkownik nie znaleziony');
error.status = 404;
return next(error);
}
res.json({ user });
} catch (err) {
next(err); // Przekaż błąd do error handlera
}
});
// Custom error class
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.isOperational = true;
}
}
// Middleware 404
app.use((req, res, next) => {
next(new AppError('Nie znaleziono', 404));
});
// Centralny error handler
app.use((err, req, res, next) => {
console.error('Error:', err.message);
const statusCode = err.statusCode || err.status || 500;
res.status(statusCode).json({
success: false,
error: {
message: err.message || 'Wewnętrzny błąd serwera',
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
}
});
});
Jak obsługiwać błędy w middleware asynchronicznym?
Odpowiedź w 30 sekund:
Express nie przechwytuje automatycznie błędów w async/await. Trzeba używać try/catch i przekazywać błędy przez next(err), lub użyć wrappera asyncHandler do automatycznego przechwytywania.
Odpowiedź w 2 minuty:
Express 4.x nie przechwytuje automatycznie błędów z async/await, co jest częstą pułapką. Odrzucony Promise powoduje UnhandledPromiseRejection zamiast wywołać error handler. Rozwiązaniem jest try/catch lub wrapper asyncHandler, który automatycznie przekazuje błędy do next().
// ❌ ŹLE - błąd nie zostanie obsłużony
app.get('/api/data', async (req, res) => {
const data = await fetchData(); // Jeśli rzuci błąd...
res.json(data);
});
// ✅ DOBRZE - try/catch
app.get('/api/data', async (req, res, next) => {
try {
const data = await fetchData();
res.json(data);
} catch (error) {
next(error);
}
});
// ✅ LEPIEJ - wrapper asyncHandler
const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
app.get('/api/users', asyncHandler(async (req, res) => {
const users = await User.findAll();
res.json({ users });
}));
app.post('/api/users', asyncHandler(async (req, res) => {
const user = await User.create(req.body);
res.status(201).json({ user });
}));
W Express 5.x błędy async będą przechwytywane automatycznie.
Bezpieczeństwo
Jakie są najważniejsze praktyki bezpieczeństwa w Express.js?
Odpowiedź w 30 sekund: Kluczowe praktyki to: używanie Helmet do ustawiania nagłówków bezpieczeństwa, walidacja inputu, rate limiting, HTTPS, sanityzacja danych, ukrywanie informacji o serwerze i regularne aktualizacje zależności.
Odpowiedź w 2 minuty:
Bezpieczeństwo aplikacji Express wymaga wielowarstwowego podejścia. Najważniejsze to ochrona przed typowymi atakami (XSS, CSRF, injection), ograniczenie rate limitingu, walidacja inputu i bezpieczna konfiguracja nagłówków HTTP. Większość tych zabezpieczeń można dodać przez gotowe middleware.
const express = require('express');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const app = express();
// 1. Helmet - nagłówki bezpieczeństwa
app.use(helmet());
// 2. Ukryj informacje o Express
app.disable('x-powered-by');
// 3. Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minut
max: 100, // 100 żądań na IP
message: { error: 'Zbyt wiele żądań' }
});
app.use('/api/', limiter);
// 4. Limit rozmiaru body
app.use(express.json({ limit: '10kb' }));
// 5. Walidacja inputu
const validateUser = (req, res, next) => {
const { email, password } = req.body;
if (!email || !email.includes('@')) {
return res.status(400).json({ error: 'Nieprawidłowy email' });
}
if (!password || password.length < 8) {
return res.status(400).json({ error: 'Hasło za krótkie' });
}
next();
};
app.post('/register', validateUser, registerHandler);
// 6. Bezpieczne nagłówki CORS
const cors = require('cors');
app.use(cors({
origin: 'https://mojadomena.pl',
methods: ['GET', 'POST'],
credentials: true
}));
Jak skonfigurować CORS w Express.js?
Odpowiedź w 30 sekund:
CORS konfiguruje się pakietem cors lub ręcznie ustawiając nagłówki. Pozwala kontrolować, które domeny mogą wysyłać żądania do API, jakie metody HTTP i nagłówki są dozwolone.
Odpowiedź w 2 minuty:
CORS (Cross-Origin Resource Sharing) to mechanizm bezpieczeństwa przeglądarek, który blokuje żądania z innych domen. W Express konfiguruje się go pakietem cors lub ręcznie ustawiając nagłówki. Konfiguracja powinna być restrykcyjna na produkcji - zezwalaj tylko na znane domeny.
const cors = require('cors');
// Podstawowa konfiguracja - wszystkie origin
app.use(cors());
// Zaawansowana konfiguracja
app.use(cors({
origin: ['https://frontend.pl', 'https://admin.pl'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 86400 // Cache preflight na 24h
}));
// Dynamiczny origin
app.use(cors({
origin: (origin, callback) => {
const allowedOrigins = ['https://frontend.pl'];
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Niedozwolony origin'));
}
}
}));
// Ręczna konfiguracja (bez pakietu)
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'https://frontend.pl');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (req.method === 'OPTIONS') {
return res.sendStatus(200);
}
next();
});
Zobacz też
- Kompletny Przewodnik - Rozmowa Node.js Backend Developer - pełny przewodnik przygotowania do rozmowy Node.js
- Jak Przygotować się do Rozmowy Node.js - praktyczne porady
- Wzorce i Architektura Backend - wzorce projektowe dla backendu
- SQL Pytania Rekrutacyjne - przygotowanie do pytań z baz danych
Ten artykuł jest częścią serii przygotowującej do rozmów rekrutacyjnych na stanowisko Node.js Backend Developer. Sprawdź nasze fiszki Express.js z 42 pytaniami i odpowiedziami do nauki.
Chcesz więcej pytań rekrutacyjnych?
To tylko jeden temat z naszego kompletnego przewodnika po rozmowach rekrutacyjnych. Uzyskaj dostęp do 800+ pytań z 13 technologii.
