What you'll learn

By the end of this you'll have solid answers for the Node.js questions that repeat in backend loops — the event loop, async error paths, streams, modules, and what breaks when you scale. More importantly, you'll know why those answers are true, which is what follow-up questions test.

Node interviews tend to be opinionated. "It works on my machine" is not enough; interviewers want to hear what happens under load when the event loop stalls or a Promise rejects with no handler.

Who this is for

  • Backend or full-stack candidates where Node is the runtime
  • Developers who write Express or Fastify daily but fuzz the event loop explanation
  • Anyone who got "tell me about async in Node" and answered with only "callbacks bad, async/await good"

You can skip this if the role is pure infrastructure (Kubernetes, Terraform) with almost no application runtime discussion. Those loops weight orchestration over V8.

What Node.js interviews are really about

Plain English: Node interviews check whether you understand that one thread runs your JavaScript and that I/O is delegated so that thread can serve many requests — until you block it.

They are not asking you to recite libuv phase names for fun. They want to know you'll avoid sync file reads on hot paths, handle Promise rejections deliberately, and pick streams when buffering a 2 GB upload would crash the process.

Prerequisites

  • JavaScript async: Promises, async/await, error propagation
  • Basic HTTP: methods, status codes, request/response shape
  • One framework touched in anger (Express, Fastify, or Nest)
  • Comfort reading simple package.json and module imports

No C++ required. Knowing that libuv exists and handles async I/O is enough unless you're interviewing for platform teams.

Setup from zero

Step 1 — Rebuild the event loop story

Write four sentences from memory: single JS thread, call stack, microtasks vs macrotasks, I/O completion queues callbacks. Say it to another human or a voice memo. If you stumble, reread the mental model section and repeat until smooth.

Step 2 — Collect three war stories

Real or realistic: a production incident involving slow endpoints, a memory leak, or an unhandled rejection. Interviewers remember candidates who connect theory to something that broke at 2 a.m.

Step 3 — Trace one request end to end

From curl hitting your server through middleware, handler, database, JSON response. You'll get "what happens when this route is called" — having a concrete path beats abstract Node trivia.

Little tip: mentioning graceful shutdown on SIGTERM — stop accepting, drain in-flight, close DB — separates people who ran production services from people who only ran node index.js locally.

The mental model

One thread, non-blocking I/O, work when ready.

When Node hits I/O, it hands work to the OS/libuv and continues. When I/O finishes, callbacks enter queues. The event loop runs them when the call stack is empty. Microtasks (Promises) drain before the next macrotask phase.

Anything CPU-heavy on that thread — big JSON.parse, crypto loops, sync fs.readFileSync — blocks every client. That single idea explains most performance and concurrency questions.

Key terms

Event loop — schedules callback execution when the stack is clear.

Microtask queue — Promise reactions; runs before next timer/I/O phase.

libuv — native layer handling async I/O and the thread pool for some file/DNS work.

Backpressure — slowing producers when consumers cannot keep up; central to streams.

Unhandled rejection — rejected Promise with no .catch; can crash modern Node processes.

Step-by-step

Q: Explain the event loop.

Answer: JavaScript runs on one thread. Sync code runs to completion. Async I/O callbacks and Promise reactions queue up. Promises (microtasks) run before setTimeout and I/O callbacks (macrotasks) in the classic ordering puzzle.

Follow-up: setImmediate vs setTimeout(0) — different phases; setImmediate often runs after I/O in the same tick cycle. Honest line: "I know the ordering rules; I reach for neither in app code unless there's a specific reason."

---

Q: What blocks the event loop?

Answer: Long synchronous CPU work and sync I/O. Example: parsing a 50 MB JSON file synchronously on every request freezes all clients. Fix: async I/O, streaming parsers, Worker Threads for CPU-bound work, or move heavy jobs to another service.

---

Q: How do you handle errors in async code?

Answer: try/catch around await; .catch on chains; centralized Express error middleware with next(err). Log with context (route, user id). Never swallow errors silently.

Follow-up: Global unhandledRejection handler to log and exit — last resort, not a substitute for local handling.

---

Q: Streams — why?

Answer: Process data in chunks with bounded memory. Pipe file read to HTTP response instead of loading whole file. Use pipeline from stream/promises for cleanup on error.

---

Q: require vs import?

Answer: CommonJS vs ESM — sync vs static analysis, interop rules in package.json "type" field. New projects often ESM; many npm packages still CJS.

---

Q: How does Node handle concurrency without threads?

Answer: Concurrent I/O, not parallel JS (unless Worker Threads/cluster). While one request waits on DB, others progress. CPU-bound work needs workers or separate processes.

---

Q: How would you debug a slow Node API in production?

Answer: Start with metrics — p95 latency per route, error rate, event loop lag if exposed. Trace one slow request: DB query time vs handler logic vs external API. Check connection pool exhaustion, N+1 queries, missing indexes, and sync work on the hot path. Profile with --inspect or APM if CPU-bound.

Follow-up: "Memory growing over days?" — suspect event listeners, unclosed intervals, caches without bounds. Take heap snapshot, compare generations, fix the retention path.

---

Q: Where does middleware fit in the request lifecycle?

Answer: Express-style stacks run middleware in order: parsing body, auth, logging, then route handler, then error middleware with four args. Async middleware must call next() or return a response — forgotten next hangs the client. Error middleware only runs if you pass errors with next(err).

Working examples

Async handler with proper error propagation

app.get('/users/:id', async (req, res, next) => {
  try {
    const user = await db.findUser(req.params.id);
    if (!user) return res.status(404).json({ error: 'not found' });
    res.json(user);
  } catch (err) {
    next(err); // centralized handler logs + 500
  }
});

Stream a large file instead of buffering

import { createReadStream } from 'fs';
import { pipeline } from 'stream/promises';

app.get('/export', (req, res, next) => {
  pipeline(createReadStream('big.csv'), res).catch(next);
});

Patterns and when to use them

| Pattern | When |
|---------|------|
| async/await in handlers | Default for route logic |
| Job queue (BullMQ, etc.) | Email, reports, retries, spike absorption |
| Cluster module / multiple replicas | Multi-core machines; prefer container replicas in cloud |
| Worker Threads | CPU-heavy same-process tasks |
| Caching (Redis) | Read-heavy, tolerable staleness |

Little tip: if asked "how would you scale this Node API," start with stateless app servers behind a load balancer, external session store, and database read replicas before jumping to microservices. Interviewers reward boring scaling that works.

Common mistakes

Confusing async with fast. Async is non-blocking, not magically quicker.

try/catch around non-awaited Promise. Fire-and-forget async still needs .catch.

Loading huge payloads into memory. Always ask "could this be a stream?"

No graceful shutdown. Deployments drop connections; mention server.close() and draining.

Ignoring memory leaks. Listeners not removed, closures holding big buffers — mention heap snapshots if you've done it.

Connection pool misconfiguration. Too few connections → queue latency; too many → DB overload. Say you'd tune pool size against DB limits and monitor wait time.

Logging without structure. JSON logs with request id, route, and duration beat console.log sprawl when debugging production traffic.

Troubleshooting

Event loop question anxiety: Draw stack → microtasks → macrotasks. Walk through console.log ordering once calmly.

"We use Nest/Deno/Bun" — core loop story still applies; mention your runtime's differences briefly, then return to fundamentals.

Senior scaling follow-ups: Talk bottlenecks — DB, cache, queue — with numbers, even rough ones.

Asked to implement rate limiting: Token bucket or sliding window in Redis; return 429 with Retry-After; mention per-user vs per-IP keys.

Database question in Node round: ORMs hide SQL but not latency — mention indexes, connection pooling, and read replicas when reads dominate.

Checklist

  • [ ] Can explain microtask vs macrotask ordering with an example
  • [ ] Can name two things that block the event loop
  • [ ] Can show async error handling in Express-style code
  • [ ] Know when streams beat readFile
  • [ ] Have one production-ish story ready
  • [ ] Mention graceful shutdown and unhandled rejections

Practice task

Write a 10-line Express route that reads a query param, calls a fake async DB function, returns JSON, and forwards errors to middleware. Say aloud what happens from TCP packet to response. Then add what breaks if the DB call takes 30 seconds and you have 500 concurrent requests — and one fix.

FAQ

Do I need to know C++ or V8 internals?
Rarely for application backend roles. Know the one-thread model.

NestJS vs Express?
Know modules, DI, and how you'd structure a growing API. Fundamentals still event loop + async.

Testing in interviews?
Supertest for HTTP, jest/vitest for units. Mention testing error paths, not only 200s.

Bun/Deno?
Nice to acknowledge; most loops still center Node unless the company says otherwise.

How much Express vs raw Node?
Know middleware, routing, and error handling patterns. Raw http.createServer is rare in app interviews but shows you understand what's underneath.

Security basics in Node rounds?
Mention input validation, parameterized queries, rate limiting, helmet-style headers, and never logging secrets — often a follow-up after auth questions.

What to learn next

  • System design interview starter — backend loops often escalate to scale
  • JavaScript deep cuts — async questions start in JS, end in Node
  • React interview questions — full-stack roles mix both
  • [System design interview starter](/interview/system-design-interview-starter)
  • [JavaScript interview deep cuts](/interview/javascript-interview-deep-cuts)
  • [React interview questions](/interview/react-interview-questions)

Takeaways

Node interviews hinge on the event loop story and disciplined async error handling. Streams and shutdown details separate mid from senior signals.

Prepare stories, trace one real request, and practice ordering questions out loud. Specific beats vague every time.

If you remember only one thing: never block the single JavaScript thread on hot paths — async I/O, streams, and workers exist because everything shares that thread.