Otázky a odpovědi k pohovoru z Node.js pro rok 2026
Cílená sada otázek k pohovoru z Node.js pro backendové vývojáře. Odpovědi jsou strukturovány pro nácvik mluveného projevu: vysvětlete architekturu, uveďte příklad a zmiňte kompromisy v produkčním nasazení.
Node.js je běhové prostředí pro JavaScript mimo prohlížeč, postavené na enginu V8 a rozšířené o API (Application Programming Interface) pro sítě, soubory, procesy, datové toky (streams) a přístup k operačnímu systému. Běžně se používá pro REST API, služby GraphQL, servery WebSocket v reálném čase, mikroslužby, nástroje CLI (Command Line Interface), proxy servery a API brány (API gateways). Jeho hlavní předností jsou neblokující V/V operace (non-blocking I/O): jeden proces dokáže efektivně obsloužit mnoho síťových připojení. Nevýhodou je, že výpočty náročné na procesor mohou zablokovat smyčku událostí (event loop), pokud nejsou přesunuty do pracovních vláken (workers), samostatných procesů nebo jiné služby.
JavaScript běží v enginu V8, zatímco API (Application Programming Interface) v Node.js delegují asynchronní práci na operační systém nebo na fond vláken knihovny libuv. Hlavní vlákno JavaScriptu nečeká na dokončení I/O (Input/Output) operace; pokračuje ve vykonávání dalšího kódu. Jakmile se operace dokončí, její zpětné volání (callback) nebo pokračování pomocí `Promise` je připraveno pro smyčku událostí (event loop). Tímto způsobem Node.js dosahuje vysoké souběžnosti, přestože uživatelský JavaScript obvykle běží v jednom hlavním vlákně.
3Co jsou to V8, knihovna libuv a smyčka událostí (event loop)?
V8 kompiluje a vykonává JavaScript a spravuje javascriptové objekty a paměť. Knihovna libuv poskytuje multiplatformní smyčku událostí, integraci asynchronního I/O (Input/Output) a fond vláken (worker thread pool) pro operace, jako je práce se souborovým systémem, překlad DNS (Domain Name System) a kryptografie. Smyčka událostí rozhoduje o tom, kdy se mají spustit zpětná volání (callbacks) dokončených operací. Samotný Node.js propojuje API JavaScriptu s nativními možnostmi systému; samotný engine V8 neposkytuje HTTP server, přístup k souborovému systému ani API pro práci s procesy.
4Proč je Node.js často označován jako jednovláknový (single-threaded)?
Jediný proces Node.js obvykle vykonává uživatelský JavaScript v jednom hlavním vlákně, takže dva běžné obslužné programy požadavků (request handlers) nespouštějí JavaScript v tomto vlákně přesně ve stejný okamžik. Platforma však doslovně není jen jedním vláknem: knihovna `libuv` má fond vláken, operační systém provádí asynchronní I/O (Input/Output) operace, jádro V8 vykonává práci pro GC (Garbage Collector) a Node.js také podporuje `worker_threads` a dceřiné procesy. Tento pojem popisuje především výchozí model vykonávání jazyka JavaScript.
5Jak dokáže Node.js souběžně zpracovávat velké množství požadavků?
Node.js zahájí I/O (Input/Output) operace a poté se vrátí do smyčky událostí (event loop), místo aby blokoval jedno vlákno pro každý požadavek. Například může probíhat 1 000 volání databáze, zatímco hlavní vlákno nadále přijímá nová spojení a zpracovává připravená zpětná volání (callbacks). Tento přístup funguje skvěle pro zátěž vázanou na I/O (I/O-bound workloads), jako jsou volání HTTP (Hypertext Transfer Protocol), databázové dotazy, operace se soubory a sokety. Naopak funguje špatně, když obsluhy požadavků vykonávají náročnou synchronní práci na CPU (Central Processing Unit), protože to zablokuje smyčku událostí a zpozdí všechny ostatní požadavky.
6Jaké jsou hlavní fáze smyčky událostí (event loop) v Node.js?
Smyčka událostí (event loop) knihovny libuv zahrnuje fáze jako jsou časovače (timers), čekající zpětná volání (pending callbacks), idle/prepare, poll, check a zpětná volání pro uzavření (close callbacks). Fáze časovačů spouští zpětná volání funkcí `setTimeout` a `setInterval`; fáze poll zpracovává mnoho I/O událostí; fáze check spouští zpětná volání `setImmediate`; fáze close zpracovává události uzavření, jako je zavírání socketů. Node.js má také fronty mikrouloh (microtask queues), včetně `process.nextTick` a mikrouloh objektu `Promise`, které se spouštějí mezi jednotlivými zpětnými voláními. Příliš mnoho mikrouloh může zahltit I/O operace a způsobit, že se služba bude jevit jako zamrzlá.
7Jak se liší synchronní, asynchronní a neblokující operace?
Synchronní operace vrátí výsledek před spuštěním dalšího řádku a zablokuje hlavní vlákno, jako například `fs.readFileSync` v obslužné rutině požadavku (request handler). Asynchronní operace vrátí svůj výsledek později prostřednictvím zpětného volání (callback) nebo objektu `Promise`. Neblokující (non-blocking) znamená, že vlákno může pokračovat v jiné práci, zatímco operace čeká na dokončení. Tyto termíny spolu souvisí, ale nejsou totožné: asynchronní popisuje, jak je doručen výsledek, zatímco neblokující popisuje, zda vlákno čeká. Obslužné rutiny požadavků v backendu by měly obecně preferovat asynchronní neblokující API (Application Programming Interface).
8Co je to vyhladovění smyčky událostí (event loop starvation)?
K vyhladovění smyčky událostí (event loop starvation) dochází, když je smyčka událostí neustále vytížena zpracováním JavaScriptového kódu nebo mikrouloh (microtasks), a nemůže tak včas zpracovávat I/O (Input/Output) operace, časovače nebo nové požadavky. Běžnými příčinami jsou náročné synchronní výpočty, zpracování obrovských souborů ve formátu JSON (JavaScript Object Notation), neomezené řetězce objektů typu Promise a nadměrné používání process.nextTick. Mezi řešení patří rozdělení práce na menší části, omezení velikosti vstupu, profilování využití procesoru (CPU - Central Processing Unit), přesunutí úloh náročných na CPU do worker_threads nebo jiné služby a vyhýbání se synchronním API (Application Programming Interface) v často vykonávaných částech kódu kritických z hlediska výkonu.
9Co je zpětné volání (callback) a co je takzvané peklo zpětných volání (callback hell)?
Zpětné volání (callback) je funkce předaná jako argument, která se má spustit později, často po dokončení asynchronní operace. Klasická zpětná volání v Node.js běžně dodržují styl, kdy je chyba na prvním místě (error-first): `callback(error, result)`. K "callback hell" dochází, když jsou závislé operace hluboce zanořené, což ztěžuje čitelnost zpracování chyb a toku řízení (control flow). Řešením je rozložení kódu na pojmenované funkce, použití `Promise` nebo `async/await` a souběžné spouštění nezávislých operací pomocí `Promise.all` namísto jejich zanořování.
import fs from 'node:fs';
fs.readFile('file.txt', 'utf8', (err, data) => {
if (err) {
console.error(err);
return;
}
console.log(data);
});
Objekt `Promise` představuje budoucí výsledek asynchronní operace a může se nacházet ve stavech čekající (pending), splněno (fulfilled) nebo zamítnuto (rejected). Funkce označená jako `async` vždy vrací `Promise`. Klíčové slovo `await` pozastaví pouze aktuální `async` funkci, dokud se `Promise` nevyřeší; neblokuje celý proces Node.js. Dobrá odpověď by měla zmínit zpracování chyb pomocí `try/catch`, vyvarování se takzvaných plovoucích (floating) `Promise` a využití `Promise.all` pro nezávislé úlohy, aby operace neprobíhaly zbytečně sekvenčně.
11Jak se liší `process.nextTick`, `queueMicrotask`, `setImmediate` a `setTimeout`?
`process.nextTick` se spustí po aktuální operaci a předtím, než smyčka událostí (event loop) přejde do další fáze. `queueMicrotask` naplánuje standardní JavaScriptovou mikroulohu (microtask), podobně jako pokračování objektů `Promise`. `setImmediate` se spouští ve fázi kontroly (check phase). `setTimeout(fn, 0)` se spouští ve fázi časovačů (timers) po uplynutí minimálního zpoždění. Obvykle se `nextTick` spustí před mikroulohami `Promise` a pořadí vykonání `setTimeout(0)` a `setImmediate` závisí na kontextu; po zpětných voláních (callbacks) vstupně-výstupních operací (I/O) se často jako první spustí `setImmediate`.
Synchronní kód využívá `try/catch`. Kód založený na objektech `Promise` by měl používat `await` uvnitř bloku `try/catch` nebo připojit metodu `.catch`. Zpětná volání ve stylu "error-first" (error-first callbacks) by měla před použitím výsledku zkontrolovat argument obsahující chybu. V serverových aplikacích je důležitý centrální middleware pro zpracování chyb, logování kontextu požadavků, bezpečné zprávy pro klienty a řízené ukončení (graceful shutdown). Událost `uncaughtException` by se neměla používat k udržení poškozeného procesu v běhu; obvykle slouží jako signál k zalogování chyby, uvolnění prostředků a restartu.
Modul je izolovaná jednotka kódu s explicitně exportovaným rozhraním. Node.js podporuje vestavěné moduly, jako jsou `node:fs` a `node:http`, lokální projektové moduly, balíčky npm (Node Package Manager), moduly CommonJS a moduly ECMAScript. Moduly zpřehledňují hranice zodpovědností a umožňují znovupoužitelnost. Kompromisem v produkčním prostředí je úroveň provázanosti (coupling): pokud se moduly navzájem importují příliš hluboce nebo skrývají vedlejší efekty (side effects) během importu, je složitější porozumět jejich testování a chování při startu.
// math.js
export function add(a, b) {
return a + b;
}
// app.js
import { add } from './math.js';
14Jak se liší require v CommonJS a import v ES (ECMAScript) Modules?
CommonJS používá require a module.exports, zatímco ES moduly používají import a export. ES moduly podporují statickou analýzu a použití await na nejvyšší úrovni (top-level await) a jsou vybírány prostřednictvím type: module v package.json nebo pomocí souborů s příponou .mjs. CommonJS se často načítá synchronně a může být označen příponou .cjs. V moderních Node.js projektech existují oba systémy, takže kvalitní odpověď se vyhne přílišnému zjednodušení a vysvětlí, že režim modulu je určen příponou souboru, souborem package.json a kontextem projektu.
npm je správce balíčků a rozhraní příkazové řádky (CLI - Command Line Interface). Soubor package.json popisuje skripty, metadata, závislosti (dependencies) a vývojové závislosti (devDependencies). Soubor package-lock.json zaznamenává přesný strom závislostí, takže jsou instalace reprodukovatelné. V CI (Continuous Integration) se obvykle preferuje příkaz npm ci, protože instaluje striktně ze souboru lock a selže, pokud se package.json a lock soubor neshodují. To chrání sestavení před náhodným posunem závislostí (dependency drift).
16Jak by se měla spravovat konfigurace a proměnné prostředí?
Node.js čte proměnné prostředí přes `process.env`, ale hodnoty jsou řetězce a měly by být při spuštění validovány a převedeny. Tajemství (secrets) by se neměla ukládat v repozitáři, soubor `.env` by měl být ignorován a produkční tajemství by měla pocházet z platformy nebo ze správce tajemství (secret manager). Dobře navržené služby rychle selžou (fail fast), pokud chybí požadovaná konfigurace. `NODE_ENV` je konvence, nikoliv bezpečnostní hranice, takže by se na ni kód neměl spoléhat jako na chráněný mechanismus.
const port = Number(process.env.PORT ?? 3000);
const databaseUrl = process.env.DATABASE_URL;
if (!databaseUrl) {
throw new Error('DATABASE_URL is required');
}
`EventEmitter` implementuje návrhový vzor publish-subscribe uvnitř procesu Node.js. Metoda `on` registruje posluchače (listener), `once` registruje posluchače pro jednu událost, `off` odebírá posluchače a `emit` synchronně volá posluchače v pořadí jejich registrace. Synchronní povaha je důležitá: pomalý posluchač zablokuje emitter. Událost `error` je speciální, protože neobsloužená událost `error` může způsobit pád procesu. Dlouhodobě běžící systémy by také měly odebírat nepoužívané posluchače, aby se zabránilo únikům paměti (memory leaks).
import { EventEmitter } from 'node:events';
const emitter = new EventEmitter();
emitter.on('orderCreated', order => {
console.log(`Order ${order.id} created`);
});
emitter.emit('orderCreated', { id: 42 });
`Buffer` představuje posloupnost bajtů o pevné velikosti. Používá se pro soubory, TCP sokety, těla HTTP požadavků, obrázky, kryptografii a binární protokoly. `Buffer.from` vytvoří buffer z existujících dat, `Buffer.alloc` vytvoří paměť vyplněnou nulami a `Buffer.allocUnsafe` je rychlejší, ale může obsahovat starou paměť, dokud není přepsána. Z toho důvodu je `allocUnsafe` vhodné použít pouze tehdy, když kód buffer okamžitě naplní a nikdy nevystaví neaktuální data.
19Co jsou to proudy (streams) a jaké typy existují?
Proudy zpracovávají data po částech (chunks) místo toho, aby načítaly všechno do paměti. Hlavní typy jsou Readable, Writable, Duplex a Transform. Běžnými příklady jsou soubory, objekty HTTP (Hypertext Transfer Protocol) požadavků a odpovědí, TCP (Transmission Control Protocol) sockety a gzip proudy. Proudy jsou cenné pro velké soubory a síťový provoz, protože snižují využití paměti a latenci. Odpověď pro produkční úroveň by měla zmínit, že chyby v proudech musí být správně ošetřeny napříč celým řetězcem.
20Co je to zpětný tlak (backpressure) a proč je užitečná funkce `pipeline`?
Ke zpětnému tlaku (backpressure) dochází, když zdroj dat produkuje části dat (chunks) rychleji, než je cíl dokáže spotřebovat. Bez zpětného tlaku může spotřeba paměti růst, dokud se proces nezpomalí nebo nespadne. Proudy (streams) a funkce `pipe` dokážou koordinovat tok, ale funkce `pipeline` z modulu `node:stream/promises` je bezpečnější pro vícekrokové řetězce, protože propaguje chyby, uzavírá související proudy a vrací `Promise`. Je to dobrá výchozí volba pro kompresi souborů, nahrávání, stahování a transformace proudů.
import fs from 'node:fs';
import zlib from 'node:zlib';
import { pipeline } from 'node:stream/promises';
await pipeline(
fs.createReadStream('input.txt'),
zlib.createGzip(),
fs.createWriteStream('input.txt.gz')
);