TL;DR: The best Node.js developer interview gives the candidate real code: a flaky API call to harden, a leaking route to fix, a pull request to review. These 25 questions come with what a strong answer includes and what to probe next, plus the 2025 and 2026 changes (Express 5, NestJS 12, npm 12) a current candidate should know.
npm 12 shipped on July 8, 2026 and stopped running dependency install scripts unless the project allows them, ten months after the Shai-Hulud worm spread through more than 500 npm packages.
A Node.js hire now owns part of the supply chain as well as the code. The questions below test that, along with the day-to-day work: APIs, queues, databases and production incidents.
- 1A 60-minute live exercise on a small, planted-bug repo shows more than an hour of definitions. Five exercises with scoring notes are below.
- 2Express 5 forwards rejected promises to the error handler, so the async wrapper most Express 4 codebases carry is no longer needed.
- 3NestJS 12, released August 27, 2026, ships ESM packages and scaffolds new projects with Vitest instead of Jest.
- 4Node.js 20 reached end of life on April 30, 2026. A candidate maintaining a Node.js 20 service should raise the upgrade before you ask.
This page is the interview plan. For runtime questions on the event loop, streams, require(esm) and the permission model, use our advanced Node.js interview questions.
Screening Questions
1. Tell me about a Node.js service you ran in production. What broke first?
Listen for specifics: traffic, the database, how it was deployed, and one failure the candidate diagnosed. "It was a REST API with Express" says nothing.
"About 300 requests a second, Postgres behind it, and our first outage was a connection pool exhausted by a slow report query" tells you they were there.
Probe: "How did you find out?" A strong answer names the signal (an alert, a dashboard, a customer ticket) and the fix that stopped it recurring. A candidate whose services never broke either never ran one or was not on call for it.
This question opens a four-round loop: a screening call, a live exercise, a pull request review and a design discussion. Plan which round tests each skill in depth, so no skill is tested only by a definition question.

2. Express, Fastify or NestJS: which did you use last, and what would make you switch?
There is no right framework, only a reason. Express is the default and the most familiar; version 5 is now the latest tag on npm.
Fastify validates and serializes against JSON Schema per route, which makes it faster and gives typed contracts. NestJS adds modules, dependency injection and decorators, which large teams use to keep a codebase consistent.
Good answers tie the choice to the team: "NestJS, because twelve engineers were touching one service and the structure stopped arguments." Weak answers repeat benchmark numbers.
Probe with the migration cost: what would it take to move their last service to another framework?
3. Where do you validate request data, and how do you keep the types honest?
At the edge of the service, against a schema, before any business logic runs. TypeScript types disappear at runtime, so a req.body typed as CreateOrder is a promise the client never made.
The schema should both validate and produce the type, as Zod or Valibot schemas do, or as Fastify's JSON Schema type providers and NestJS 12's Standard Schema support do.
const CreateOrder = z.object({
sku: z.string().min(1),
quantity: z.number().int().positive().max(100),
});
type CreateOrder = z.infer<typeof CreateOrder>;
app.post('/orders', async (req, res) => {
const parsed = CreateOrder.safeParse(req.body);
if (!parsed.success) return res.status(400).json({ issues: parsed.error.issues });
res.status(201).json(await orders.create(req.user.id, parsed.data));
});
Probe: "What does the endpoint do with a field it does not expect?" Stripping unknown keys is the safe default, because passing req.body straight to an ORM lets a client set isAdmin.
4. Sessions or JWTs for a Node.js API?
Sessions for a browser app on one domain, short-lived tokens for service-to-service calls and third-party clients.
A session is a random ID in an HttpOnly, Secure, SameSite cookie, looked up in Redis or the database, and it can be revoked instantly.
A JWT is verified without a lookup, which is why it cannot be revoked before it expires unless you add a denylist, which brings the lookup back.
- Random ID in an HttpOnly cookie
- One store lookup per request
- Revoked at once by deleting the record
- Signed claims, verified without a lookup
- Suits service-to-service and third-party clients
- Valid until expiry unless you add a denylist
Red flag: storing a long-lived JWT in localStorage, where any injected script can read it. For deeper token questions, see our JWT interview guide.
Live Exercises
5. "Wrap this flaky partner API call with a timeout and retries." What does a good solution include?
Give the candidate a function that calls an endpoint failing 30% of the time and sometimes hanging.
A good solution adds four things: a per-attempt timeout, retries only for errors worth retrying (timeouts, 429, 502 to 504, not 400 or 404), exponential backoff with jitter, and an overall attempt limit.
async function withRetry(fn, { attempts = 3, baseMs = 200 } = {}) {
for (let i = 1; ; i++) {
try {
return await fn(AbortSignal.timeout(2_000));
} catch (err) {
const retryable = err.name === 'TimeoutError' || [429, 502, 503, 504].includes(err.status);
if (!retryable || i === attempts) throw err;
const delay = baseMs * 2 ** (i - 1) * (0.5 + Math.random());
await new Promise((r) => setTimeout(r, delay));
}
}
}
Probe: "Is it safe to retry a POST that creates a payment?" Only with an idempotency key the partner honors. Strong candidates also honor a Retry-After header when the partner sends one.
6. "Memory on this Express app climbs under load. Find the leak." What should they catch?
Plant two leaks in a 100-line app: a module-level Map used as a cache with no size limit or expiry, and an emitter.on() call inside a request handler that is never removed. Both are common in real code and both survive code review.
const cache = new Map(); // leak 1: grows forever
app.get('/rates/:currency', async (req, res) => {
priceFeed.on('update', refresh); // leak 2: one listener per request
if (!cache.has(req.params.currency)) {
cache.set(req.params.currency, await fetchRates(req.params.currency));
}
res.json(cache.get(req.params.currency));
});
Watch the method more than the result. A strong candidate reproduces the growth with a load tool, takes two heap snapshots and compares them, rather than reading the code line by line.
The MaxListenersExceededWarning that Node prints once an emitter passes 10 listeners for one event is a clue; a candidate who notices it in the console is paying attention.
7. "Import a 2 GB CSV of orders into PostgreSQL." How should it work?
As a stream. Reading the file with fs.readFile loads 2 GB into memory and crashes a small container.
A good design streams the file through a CSV parser, validates each row, batches rows (for example 1,000 at a time) into multi-row inserts or a COPY FROM STDIN, and lets backpressure pause reading while the database catches up.
Probe the failure cases: a malformed row at line 1.2 million, a crash halfway, a re-run of the same file.
Strong answers write bad rows to an error file instead of aborting, record progress so a restart resumes, and make the import idempotent with a unique key on the order ID.
8. "Review this pull request." Which planted problems should they catch?
Plant five issues in a 60-line diff and count how many the candidate finds in 15 minutes:
- SQL built with a template string from
req.query(injection). - A missing
awaiton a database write, so errors vanish and the response goes out first. - The full request body logged, including a password field.
fs.readFileSyncinside a request handler.- No authorization check that the order belongs to
req.user.
Seniors find four or five and rank them: the injection and the missing ownership check block the merge, the sync read is a performance fix. Also note how they write the comments.
"This lets any user read any order; check order.userId" is a useful review. "Bad practice" is not.
9. "Add rate limiting to the login endpoint." What does a complete answer cover?
Limits keyed on both the account and the IP address, stored somewhere every instance can see.
An in-memory counter works on one process and fails silently behind a load balancer with four replicas, so production limits live in Redis or the API gateway.
A complete answer returns 429 with a Retry-After header, avoids telling an attacker whether the account exists, and thinks about the other side: a per-account limit alone lets an attacker lock a real user out.
Probe: "An attacker rotates through 10,000 IPs. Now what?" Per-account throttling, CAPTCHA after repeated failures, and alerting on the failure rate.
10. "This webhook handler charges customers twice." How do you make it idempotent?
Payment providers deliver webhooks at least once, so the same event can arrive twice. The fix is to record each event ID under a unique constraint before acting on it:
app.post('/webhooks/payments', async (req, res) => {
const event = verifySignature(req); // reject unsigned calls
const inserted = await db.query(
'INSERT INTO processed_events (id) VALUES ($1) ON CONFLICT DO NOTHING',
[event.id],
);
if (inserted.rowCount === 0) return res.sendStatus(200); // duplicate
await jobs.enqueue('apply-payment', event); // do the work off the request
res.sendStatus(200);
});
Two more things separate strong answers: verifying the provider's signature against the raw body (a parsed and re-serialized body will not match), and acknowledging fast, because providers retry when a handler is slow.
If the candidate inserts the event ID and enqueues the job in separate steps, ask what happens when the process dies between them.
System Design
11. Design a background job system for emails and PDF generation.
Requests put jobs on a queue and return; separate worker processes consume them. Redis-backed queues such as BullMQ, or a managed queue such as SQS, are the usual choices. The design points are the same either way:
- Jobs run at least once, so handlers must be idempotent.
- Failures retry with backoff and land in a dead-letter queue after the last attempt.
- PDF rendering is CPU-heavy, so it runs in its own worker pool and cannot delay emails.
Probe: "How do you know the queue is healthy?" Queue depth, job age and failure rate, with an alert on the age of the oldest waiting job. A queue that grows quietly for six hours is the common failure.
12. Real-time updates for a dashboard: WebSockets, server-sent events or polling?
For one-way updates from server to browser, server-sent events are simpler: plain HTTP, automatic reconnection in the browser, and nothing special for most proxies. WebSockets fit two-way traffic such as chat or collaborative editing.
Polling every 30 seconds is fine when the data changes rarely.
The follow-up is scale. With several Node.js instances, a message published on one must reach clients connected to another, so a strong design adds Redis pub/sub or a broker between them, and caps connections per instance.
Our WebSocket interview guide goes deeper.
13. How do you size a database connection pool for a Node.js service?
Start from the database's limit and divide. PostgreSQL's max_connections is typically 100 by default, and node-postgres's Pool defaults to 10 clients.
Twelve replicas with the default pool already need 120 connections, before migrations, cron jobs and admin sessions.
cluster or several workers per container, multiply again.A strong candidate does that arithmetic unprompted, sets a connection timeout so requests fail fast instead of queueing forever, and mentions PgBouncer or a managed proxy once the replica count grows.
Serverless raises the stakes: every concurrent function instance can open its own pool.
14. When would you not build a service in Node.js?
When the work is CPU-bound and runs for a long time per request: video transcoding, heavy numeric computation, model training. Node.js can offload that to worker threads, but Go, Rust, or Python with native libraries usually fit better.
Node.js is strongest at I/O-heavy work: APIs, gateways, real-time connections and glue between services.
This question checks judgment. A candidate who says Node.js is right for everything, or who dismisses it because "it is single-threaded", has not worked across enough systems to know the trade.
15. How do you structure a Node.js codebase so it stays testable?
Keep HTTP, business logic and data access in separate layers, and pass dependencies in instead of importing them deep inside functions.
A route handler parses the request and calls orders.create(); the service holds the rules; a repository talks to the database.
export function createOrderService({ db, payments, clock }) {
return {
async create(userId, input) {
const total = await priceCart(db, input);
return db.orders.insert({ userId, total, createdAt: clock.now() });
},
};
}
Passing db, payments and clock as arguments means a test can hand in fakes without mocking modules. NestJS does the same with its injector. Probe: "Where would a discount rule go?"
If the answer is "in the controller", the layers exist only in the folder names.
Production and Operations
16. How do you build a Docker image for a Node.js service?
A multi-stage build: install and compile in one stage, copy only the production output and node_modules into a slim runtime image.
Install with npm ci --omit=dev from the lockfile, run as the unprivileged node user the official images provide, and start with CMD ["node", "dist/server.js"] rather than npm start, so signals reach Node instead of npm.
The detail that separates seniors: the Node.js Docker team's best-practices guide warns that Node.js "was not designed to run as PID 1" and will not respond to SIGINT and similar signals there.
The fix is docker run --init, tini or dumb-init, plus a SIGTERM handler for graceful shutdown. Our Docker interview guide covers the image side.
17. What do you log, measure and alert on for a Node.js API?
Logs: structured JSON with a request ID, route, status and duration, and never passwords, tokens or full card numbers.
Metrics: request rate, error rate and latency percentiles per route, plus two Node-specific ones, event loop delay and heap used.
Traces: OpenTelemetry spans across services, which the OpenTelemetry JavaScript SDK can add to Express, Fastify, HTTP clients and database drivers.
Alerts go on symptoms users feel: error rate and p99 latency. Probe: "Which alert woke you up most, and did you change it afterwards?" Seniors have tuned noisy alerts; juniors have only received them.
18. "p99 latency jumped from 120 ms to 2 seconds after a deploy." How do you triage it?
Run this as a spoken exercise with a fake dashboard. The first move is to compare with the previous release and roll back if the regression is clear; diagnosis can continue on a canary. Then narrow down by layer.

If event loop delay rose with it, something synchronous is blocking: a new regex, a large JSON.stringify, a sync crypto call. A CPU profile of one instance under load finds it.
If loop delay is flat but time is spent waiting, look downstream: a slower query, a missing index, a partner API, or pool exhaustion.
Strong candidates state what they would check first and why, and stop guessing once a measurement answers the question.
19. How would you upgrade ten services from Node.js 22 to 24?
One service first, as a pilot, with the version pinned in one place (.nvmrc, the Dockerfile base image and engines in package.json).
Run the test suite on both versions in CI, read the release notes for removals, and watch runtime deprecation warnings in staging, since a deprecation in one major is often a removal in the next.
Then roll out service by service behind the usual deploy checks, starting with the lowest risk. Native addons are the classic blocker, because they must be rebuilt for the new ABI.
A candidate who has done this will mention something that broke, such as a dependency that pinned an old engines range.
20. What changes when the same Node.js code runs on AWS Lambda?
The process is frozen between invocations and may be reused or discarded. AWS's Lambda best practices say to initialize SDK clients and database connections outside the handler, so warm invocations reuse them.
Work started after the response, such as a fire-and-forget log shipment, may never finish, because the environment freezes as soon as the handler returns.
Cold starts add latency proportional to bundle size and initialization work, so bundling and trimming dependencies matters more than on a server.
Database connections are the other trap: 500 concurrent invocations can mean 500 connections, which is why teams put RDS Proxy or another pooler in front.
What Changed Recently
21. What did npm 12 change, and why?
npm 12, released July 8, 2026, blocks dependency lifecycle scripts (preinstall, install, postinstall) unless the root package's allowScripts policy allows them, and it stops resolving git and remote-URL dependencies by default.
The release notes list the new flow: install, run npm install-scripts approve to record approvals, then npm rebuild.
The background is the Shai-Hulud worm. GitHub was notified on September 14, 2025 of "a self-replicating worm" spreading through compromised maintainer accounts, and removed more than 500 packages.
Install scripts were how such malware ran on developers' machines and CI runners. Probe: "Which of your dependencies need install scripts, and why?" Native addons such as bcrypt or sharp are the usual answer.
22. How has publishing to npm changed since the 2025 attacks?
Long-lived tokens are being phased out.
GitHub's plan moved publishing to three methods: local publishing with two-factor authentication, granular tokens with short lifetimes, and trusted publishing, where CI gets a short-lived credential through OIDC instead of storing an npm token.
Per a July 2026 changelog, tokens that bypass 2FA lose sensitive account permissions from August 2026 and the ability to publish in January 2027.
This matters for any team that publishes internal or open-source packages. A candidate who maintains one should know whether their CI uses trusted publishing, and what breaks in January 2027 if it does not.
23. What changed in Express 5 that affects existing code?
The migration guide lists several changes that break Express 4 apps:
- Async errors: a rejected promise from a handler or middleware now goes to the error handler, as if
next(err)were called. - Route syntax: a wildcard must be named,
/*splatinstead of/*, and/{*splat}also matches the root. - Request objects:
req.bodyisundefineduntil a body parser runs, andreq.queryis a read-only getter using the "simple" parser by default. - Removed methods:
app.del(),req.param()andres.send(status)are gone.
Express 5 needs Node.js 18 or later. Ask the candidate which of these would break their last Express app. Someone who has upgraded will name the wildcard routes.
24. What is new in NestJS 12?
According to the v12.0.0 release notes, all core packages ship as ESM, and existing CommonJS apps keep working through Node.js's require(esm).
Route decorators such as @Body() accept a Standard Schema, so Zod, Valibot or ArkType schemas can validate input next to the class-validator workflow. The new nest upgrade command applies most of the migration.
New projects default to ESM, Vitest and oxlint; existing projects keep Jest until they switch. NestJS 12 requires Node.js 20.19+ or 22.12+. For a NestJS-specific loop, see our NestJS interview guide.
25. Node.js 20 is end of life. What should a team still running it do?
Plan the move to Node.js 24, the Active LTS line supported until April 2028, or 22 if a dependency blocks 24. Node.js 20 stopped receiving fixes on April 30, 2026, per the Node.js end-of-life page.
The tooling is also leaving it behind: npm 12 supports Node.js 22.22.2, 24.15.0 or 26.0.0 and later, and Vitest 5 requires Node.js 22.12.
A strong candidate turns this into a plan: inventory which services run 20, upgrade the lowest-risk one first, and budget time for native addons and frameworks with their own minimums.
Signs of a Strong Answer
- They describe incidents with numbers: request rates, pool sizes, how long the outage lasted and what alert fired.
- In exercises, they reproduce a problem and measure it before changing code.
- They ask whether an operation is safe to retry and reach for idempotency keys without prompting.
- They do connection and replica arithmetic out loud when designing anything that touches a database.
- In a code review, they rank findings by risk and write comments another engineer can act on.
- They know which of their dependencies run install scripts, and whether their services are on a supported Node.js line.
Hiring Node.js Developers
Run the screening questions in a 30-minute call, then one live exercise and one design question in a 90-minute technical round. For a full-stack role, pair this with our full stack developer interview plan.
Second Talent places pre-vetted Node.js developers from Asia who have passed exercises like these. See what a Node.js developer costs, or tell us the role and we send a shortlist.
Ready to hire? Hire NodeJS Developers: vetted AI-native talent, with first profiles in 24 hours.





