Forberedelse til Node.js backend-interview

Node.js-interviewspørgsmål og svar 2026

Et målrettet sæt af Node.js-interviewspørgsmål til backendudviklere. Svarene er struktureret til mundtlig øvelse: forklar arkitekturen, giv et eksempel og nævn kompromiser i produktionsmiljøer.

Start Node.js-prøvesamtaleIntet kreditkort kræves. 1 gratis session er tilgængelig.
Øv tekniske jobsamtaler på engelskEn tilstand, hvor ikke-modersmålstalende kan øve sig i at bestå tekniske interviews.

Arkitektur og Event Loop

1Hvad er Node.js, og hvad bruges det til?

Node.js er et JavaScript-kørselsmiljø uden for browseren, bygget på V8 og udvidet med API'er (Application Programming Interfaces) til netværk, filer, processer, streams og adgang til operativsystemet. Det bruges ofte til REST API'er, GraphQL-tjenester, realtids WebSocket-servere, mikrotjenester, CLI-værktøjer, proxyer og API-gateways. Dets primære styrke er ikke-blokerende I/O: én proces kan håndtere mange netværksforbindelser effektivt. Kompromiset er, at lange, CPU-tunge beregninger kan blokere event-loopet, medmindre de flyttes til workers, andre processer eller en anden tjeneste.

Prøv at besvare dette spørgsmål med en AI-coach

2Hvordan fungerer Node.js internt?

JavaScript kører i V8, mens Node.js-API'er (Application Programming Interface) uddelegerer asynkront arbejde til operativsystemet eller til libuvs trådpulje. Den primære JavaScript-tråd venter ikke på, at en I/O-operation bliver færdig; den fortsætter med at udføre anden kode. Når operationen er fuldført, bliver dens callback eller `Promise`-fortsættelse klar til event-loopet. Det er sådan, Node.js opnår høj samtidighed, selvom brugerens JavaScript normalt kører på én primær tråd.

Prøv at besvare dette spørgsmål med en AI-coach

3Hvad er V8, libuv og event-loopet?

V8 kompilerer og udfører JavaScript og håndterer JavaScript-objekter og hukommelse. libuv leverer det platformsuafhængige event-loop, asynkron I/O-integration og en trådpulje til operationer som f.eks. visse filsystemopgaver, DNS og kryptografi. Event-loopet beslutter, hvornår callbacks for afsluttede operationer skal køres. Node.js selv forbinder JavaScript-API'er med native-funktioner; V8 alene tilbyder ikke en HTTP-server, filsystemadgang eller proces-API'er.

Prøv at besvare dette spørgsmål med en AI-coach

4Hvorfor kaldes Node.js ofte for enkelttrådet (single-threaded)?

En enkelt Node.js-proces udfører normalt brugerens JavaScript på én hovedtråd, så to almindelige forespørgselshåndterere kører ikke JavaScript på nøjagtig samme tid i den tråd. Platformen består dog ikke bogstaveligt talt af kun én tråd: `libuv` har en trådpulje, styresystemet udfører asynkron I/O, V8 har opgaver med garbage collection, og Node.js understøtter desuden `worker_threads` og underprocesser. Udtrykket beskriver primært standardmodellen for udførelse af JavaScript.

Prøv at besvare dette spørgsmål med en AI-coach

5Hvordan kan Node.js håndtere mange forespørgsler sideløbende (concurrently)?

Node.js starter I/O-operationer og vender derefter tilbage til begivenhedsløkken i stedet for at blokere en tråd per forespørgsel. For eksempel kan 1.000 databasekald være undervejs, mens hovedtråden fortsætter med at acceptere forbindelser og behandle klare callbacks. Dette fungerer godt til I/O-tunge arbejdsbelastninger såsom HTTP-kald, databaseforespørgsler, filer og sockets. Det fungerer dårligt, når forespørgselshåndterere udfører tungt synkront CPU-arbejde, fordi dette blokerer begivenhedsløkken og forsinker alle andre forespørgsler.

Prøv at besvare dette spørgsmål med en AI-coach

6Hvad er hovedfaserne i et Node.js event-loop?

Libuv-event-loopet inkluderer faser som `timers`, `pending callbacks`, `idle/prepare`, `poll`, `check` og `close callbacks`. I `timers`-fasen køres callbacks fra `setTimeout` og `setInterval`; `poll`-fasen håndterer mange I/O-hændelser; `check`-fasen kører `setImmediate`-callbacks; `close`-fasen håndterer lukningshændelser, såsom når sockets lukkes. Node.js har også køer til mikroopgaver (microtasks), herunder `process.nextTick` og Promise-mikroopgaver, som køres mellem callbacks. For mange mikroopgaver kan forhindre I/O i at blive behandlet og få tjenesten til at fremstå som om, den er gået i stå.

Prøv at besvare dette spørgsmål med en AI-coach

7Hvordan adskiller synkrone, asynkrone og ikke-blokerende operationer sig fra hinanden?

En synkron operation returnerer, før den næste linje kører, og blokerer hovedtråden, såsom `fs.readFileSync` i en forespørgselshåndterer. En asynkron operation returnerer sit resultat senere via et callback eller et `Promise`. Ikke-blokerende (non-blocking) betyder, at tråden kan fortsætte med andet arbejde, mens operationen afventer. Begreberne er beslægtede, men ikke identiske: Asynkron beskriver, hvordan resultatet leveres, mens ikke-blokerende beskriver, hvorvidt tråden venter. Forespørgselshåndterere i backend bør generelt foretrække asynkrone, ikke-blokerende API'er.

import fs from 'node:fs';

const syncData = fs.readFileSync('file.txt', 'utf8');

const asyncData = await fs.promises.readFile(
  'file.txt',
  'utf8'
);
Prøv at besvare dette spørgsmål med en AI-coach

8Hvad er event loop-sultning (event loop starvation)?

Event loop-sultning opstår, når JavaScript-kode eller mikrotasks (microtasks) holder event-loopet beskæftiget, så det ikke kan behandle I/O, timere eller nye forespørgsler rettidigt. Almindelige årsager inkluderer tunge synkrone beregninger, enorm JSON-behandling, grænseløse Promise-kæder og overdreven brug af process.nextTick. Løsninger inkluderer at opdele arbejdet i mindre dele, begrænse inputstørrelser, CPU-profilering, flytte CPU-krævende arbejde til worker_threads eller en anden tjeneste, samt at undgå synkrone API'er i hyppigt udførte kodeområder.

app.get('/report', (req, res) => {
  const result = performVeryHeavyCalculation();
  res.json(result);
});
Prøv at besvare dette spørgsmål med en AI-coach

Asynkron programmering og fejl

9Hvad er et callback, og hvad er callback hell?

Et callback er en funktion, der videregives til at blive udført senere, ofte efter at en asynkron operation er afsluttet. Klassiske Node.js-callbacks følger almindeligvis error-first stilen: `callback(error, result)`. Callback hell opstår, når afhængige operationer er dybt indlejrede, hvilket gør fejlhåndtering og kontrolflow svært at læse. Løsningen er at opdele koden i navngivne funktioner, bruge `Promise`-objekter eller `async/await`, og køre uafhængige operationer med `Promise.all` i stedet for at indlejre dem.

import fs from 'node:fs';

fs.readFile('file.txt', 'utf8', (err, data) => {
  if (err) {
    console.error(err);
    return;
  }

  console.log(data);
});
Prøv at besvare dette spørgsmål med en AI-coach

10Hvad er et Promise, og hvordan fungerer `async/await`?

Et Promise repræsenterer det fremtidige resultat af en asynkron operation og kan være afventende (pending), opfyldt (fulfilled) eller afvist (rejected). En `async`-funktion returnerer altid et Promise. `await` sætter kun den aktuelle `async`-funktion på pause, indtil det pågældende Promise er afgjort; det blokerer ikke hele Node.js-processen. Et godt svar bør nævne fejlhåndtering med `try/catch`, undgåelse af ikke-håndterede (floating) Promises og brugen af `Promise.all` til uafhængigt arbejde, så operationer ikke unødigt afvikles sekventielt.

async function loadUser(id) {
  try {
    const user = await repository.findById(id);
    return user;
  } catch (error) {
    console.error(error);
    throw error;
  }
}

const [user, settings] = await Promise.all([
  getUser(),
  getSettings()
]);
Prøv at besvare dette spørgsmål med en AI-coach

11Hvordan adskiller `process.nextTick`, `queueMicrotask`, `setImmediate` og `setTimeout` sig fra hinanden?

`process.nextTick` kører efter den aktuelle operation og før event-loopet går videre. `queueMicrotask` planlægger en standard JavaScript-mikroopgave, i stil med `Promise`-fortsættelser. `setImmediate` kører i `check`-fasen. `setTimeout(fn, 0)` kører i `timers`-fasen efter en minimumsforsinkelse. Normalt kører `nextTick` før `Promise`-mikroopgaver, og rækkefølgen af `setTimeout(0)` og `setImmediate` afhænger af konteksten; efter I/O-callbacks vil `setImmediate` oftest køre først.

setTimeout(() => console.log('timeout'), 0);
setImmediate(() => console.log('immediate'));
Promise.resolve().then(() => console.log('promise'));
process.nextTick(() => console.log('nextTick'));
Prøv at besvare dette spørgsmål med en AI-coach

12Hvordan bør fejl håndteres i Node.js?

Synkron kode bruger `try/catch`. Promise-baseret kode bør bruge `await` inde i `try/catch` eller tilknytte `.catch`. 'Error-first'-callbacks bør kontrollere fejlargumentet, før resultatet bruges. I serverapplikationer er centraliseret fejl-middleware, logning af forespørgselskontekst, sikre klientbeskeder og yndefuld nedlukning (graceful shutdown) vigtige. `uncaughtException` bør ikke bruges til at holde en korrumperet proces kørende; det er normalt et signal til at logge, rydde op og genstarte.

try {
  JSON.parse(input);
} catch (error) {
  handleError(error);
}

try {
  await operation();
} catch (error) {
  handleError(error);
}

operation((error, result) => {
  if (error) return handleError(error);
  return result;
});
Prøv at besvare dette spørgsmål med en AI-coach

Moduler og projektstyring

13Hvad er moduler i Node.js?

Et modul er en isoleret kodeenhed med en eksplicit eksporteret grænseflade. Node.js understøtter indbyggede moduler som `node:fs` og `node:http`, lokale projektmoduler, npm-pakker, CommonJS-moduler og ECMAScript-moduler. Moduler gør ansvarsgrænser tydeligere og muliggør genbrug. Kompromisset i produktionsmiljøer er kobling: Hvis moduler importerer hinanden for dybt eller skjuler bivirkninger på importtidspunktet, bliver test og opstartsadfærd sværere at gennemskue.

// math.js
export function add(a, b) {
  return a + b;
}

// app.js
import { add } from './math.js';
Prøv at besvare dette spørgsmål med en AI-coach

14Hvordan adskiller CommonJS require og ES Modules import sig fra hinanden?

CommonJS bruger require og module.exports, mens ES Modules bruger import og export. ES Modules understøtter statisk analyse og top-level await, og de vælges via package.json type: module eller .mjs-filer. CommonJS indlæses ofte synkront og kan markeres med .cjs. I moderne Node.js-projekter eksisterer begge systemer side om side, så et stærkt svar undgår forsimpling og forklarer, at filtypenavn, package.json og projektkontekst afgør modultilstanden.

// CommonJS
const fs = require('node:fs');
module.exports = { calculate };

// ES Modules
import fs from 'node:fs';
export { calculate };
Prøv at besvare dette spørgsmål med en AI-coach

15Hvad er npm, package.json og package-lock.json?

npm er pakkehåndteringssystemet (package manager) og dets CLI (Command Line Interface). package.json beskriver scripts, metadata, dependencies og devDependencies. package-lock.json registrerer det nøjagtige afhængighedstræ, så installationer er reproducerbare. I CI (Continuous Integration) foretrækkes normalt npm ci, fordi det installerer strengt fra lock-filen og fejler, hvis package.json og lock-filen ikke stemmer overens. Dette beskytter builds mod utilsigtet glidning i afhængigheder (dependency drift).

{
  "name": "api",
  "type": "module",
  "scripts": {
    "start": "node src/server.js",
    "test": "node --test"
  },
  "dependencies": {},
  "devDependencies": {}
}
Prøv at besvare dette spørgsmål med en AI-coach

16Hvordan bør konfiguration og miljøvariabler håndteres?

Node.js læser miljøvariabler gennem process.env, men værdierne er strenge og bør valideres og konverteres ved opstart. Hemmeligheder bør ikke gemmes i kildekodelageret, .env bør ignoreres, og produktionshemmeligheder bør komme fra platformen eller en secret manager. Gode tjenester fejler hurtigt, når påkrævet konfiguration mangler. NODE_ENV er en konvention, ikke en sikkerhedsgrænse, så koden bør ikke afhænge af den som en beskyttet mekanisme.

const port = Number(process.env.PORT ?? 3000);
const databaseUrl = process.env.DATABASE_URL;

if (!databaseUrl) {
  throw new Error('DATABASE_URL is required');
}
Prøv at besvare dette spørgsmål med en AI-coach

Events, Buffers og Streams

17Hvad er `EventEmitter`?

`EventEmitter` implementerer et publish-subscribe-mønster inde i en Node.js-proces. `on` registrerer en lytter (listener), `once` registrerer en lytter til en enkelt hændelse, `off` fjerner en lytter, og `emit` kalder lyttere synkront i den rækkefølge, de blev registreret. Den synkrone natur er vigtig: en langsom lytter blokerer emitteren. Hændelsen `error` er speciel, fordi en uhåndteret fejlhændelse kan få processen til at gå ned. Længerevarende systemer bør også fjerne ubrugte lyttere for at undgå hukommelseslæk (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 });
Prøv at besvare dette spørgsmål med en AI-coach

18Hvad bruges Buffer til?

Buffer repræsenterer en sekvens af bytes med en fast størrelse. Den bruges til filer, TCP-sockets, HTTP-bodies, billeder, kryptografi og binære protokoller. Buffer.from opretter en buffer fra eksisterende data, Buffer.alloc opretter nul-udfyldt hukommelse, og Buffer.allocUnsafe er hurtigere, men kan indeholde gammel hukommelse, indtil den overskrives. Det gør allocUnsafe velegnet, men kun når koden umiddelbart udfylder bufferen og aldrig eksponerer forældede data.

const buffer = Buffer.from('Hello', 'utf8');

console.log(buffer);
console.log(buffer.toString('utf8'));

const safe = Buffer.alloc(1024);
const fast = Buffer.allocUnsafe(1024);
Prøv at besvare dette spørgsmål med en AI-coach

19Hvad er streams, og hvilke typer findes der?

Streams behandler data i bidder (chunks) i stedet for at indlæse det hele i hukommelsen. De primære typer er `Readable`, `Writable`, `Duplex` og `Transform`. Filer, HTTP-forespørgsels- og svarobjekter (Hypertext Transfer Protocol), TCP-sockets (Transmission Control Protocol) og gzip-streams er almindelige eksempler. Streams er værdifulde til store filer og netværkstrafik, fordi de reducerer hukommelsesforbruget og ventetiden (latency). Et produktionsklart svar bør nævne, at stream-fejl skal håndteres korrekt på tværs af hele kæden.

import fs from 'node:fs';

const input = fs.createReadStream('large.log');
const output = fs.createWriteStream('copy.log');

input.pipe(output);
Prøv at besvare dette spørgsmål med en AI-coach

20Hvad er modtryk (backpressure), og hvorfor er `pipeline` nyttig?

Modtryk opstår, når en datakilde producerer datablokke hurtigere, end destinationen kan nå at forbruge dem. Uden modtryk kan hukommelsesforbruget vokse, indtil processen bliver langsommere eller går ned. Streams og `pipe` kan koordinere datastrømmen, men `pipeline` fra `node:stream/promises` er mere sikker til flertrins-kæder, fordi den videresender fejl, lukker relaterede streams og returnerer et `Promise`. Det er et godt standardvalg til filkomprimering, uploads, downloads og stream-transformationer.

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')
);
Prøv at besvare dette spørgsmål med en AI-coach