Node.js / MERN-MEAN Full Stack Developer — Interview Questions
== and ===?In short: === (strict equality) compares type and value without conversion.
==(loose equality) converts operands to a common type first, which produces surprises ("" == 0is true,null == undefinedis true,[] == falseis true).- Always use
===(except, by some style guides,x == nullto check null or undefined).
var, let and const?In short: var is function-scoped, hoisted and initialised as undefined, can be re-declared, and creates a property on the global object at top level.
letandconstare block-scoped and hoisted but in the Temporal Dead Zone until their declaration (accessing them earlier throws).- Neither can be re-declared in the same scope.
constcan't be reassigned, but the object it references can still be mutated.- Default to
const, useletwhen reassigning, avoidvar.
In short: During the creation phase of an execution context, declarations are registered before code runs.
varvariables are hoisted and initialised toundefined.- Function declarations are hoisted with their body (callable before the line they appear on).
let,constandclassare hoisted but uninitialised (TDZ → ReferenceError if used early).- Function expressions and arrow functions follow the rules of the variable they're assigned to.
In short: string, number, bigint, boolean, undefined, null and symbol.
- They are immutable and compared by value.
- Everything else (objects, arrays, functions, dates) is an object compared by reference.
typeof nullreturns "object" due to a historical bug.
null and undefined?In short: undefined means a variable has been declared but not assigned, a missing property, or a function with no return value — set by the engine.
nullis an intentional "no value" assigned by the programmer.typeof undefinedis "undefined",typeof nullis "object";null == undefinedis true butnull === undefinedis false.??and?.treat both as nullish.
In short: A concise function syntax (x => x * 2).
- Differences: no own
this(lexically inherited — great for callbacks), noargumentsobject, can't be used as constructors withnew, noprototype, and implicit return for single expressions. - Don't use them as object methods or prototype methods that need dynamic
this.
In short: A closure is a function that remembers the variables from the scope where it was created, even after that scope has finished executing.
- Example:
function counter() { let c = 0; return () => ++c; }— each returned function has its own privatec. - Practical uses: data privacy, function factories, memoisation caches, debounce/throttle timers, partial application, event handlers capturing loop values (with
let).
In short: JavaScript executes on a single thread with a call stack.
- Asynchronous operations (timers, network, events) are handled by the browser/Node APIs, which queue callbacks when done.
- When the call stack is empty, the event loop first runs all microtasks (promise reactions,
queueMicrotask), then (in browsers) lets rendering happen, then picks the next macrotask (setTimeout, I/O, UI events). - That's why a resolved promise's
.thenruns before asetTimeout(..., 0)callback.
In short: A Promise represents the eventual result of an asynchronous operation, in state pending, fulfilled or rejected; .then/.catch/.finally register reactions.
Promise.allwaits for all and rejects on the first failure.allSettledwaits for all and returns each outcome.racesettles with the first settled promise (success or failure).anyfulfils with the first success and rejects with AggregateError only if all fail.
this work in JavaScript?In short: It depends on the call site: method call → the object before the dot;
- plain function call →
undefinedin strict mode (global object otherwise); - constructor with
new→ the new instance; call/apply/bind→ explicitly set;- event listeners with regular functions → the element;
- arrow functions →
thisof the enclosing lexical scope. - Losing
thiswhen passing methods as callbacks is fixed withbindor arrow functions.
call, apply and bind?In short: All set this explicitly.
call(thisArg, a, b)invokes immediately with individual arguments.apply(thisArg, [a, b])invokes immediately with an array of arguments.bind(thisArg, a)doesn't invoke — it returns a new function withthis(and optionally leading arguments) permanently bound, useful for callbacks and event handlers.
In short: Objects inherit directly from other objects through the internal [[Prototype]] link.
- When a property isn't found on an object, the engine looks up its prototype chain.
- Constructor functions share methods via
Constructor.prototype; Object.create(proto)creates objects with a chosen prototype;- ES6
class/extendsis syntax over the same mechanism (Dog.prototype.__proto__ === Animal.prototype).
In short: Attaching a single event listener to a common ancestor instead of each child, using event bubbling and event.target (often with closest()) to identify which child was interacted with.
- Benefits: fewer listeners (memory/performance), works for elements added later dynamically, simpler cleanup.
- Note: some events don't bubble (
focus,blur— usefocusin/focusout).
Debounce delays execution until calls stop for a given time (search-as-you-type, auto-save, resize end). Throttle guarantees execution at most once per interval (scroll position, mousemove, rate-limited API calls).
const debounce = (fn, ms) => { let t; return (...a) => { clearTimeout(t); t = setTimeout(() => fn(...a), ms); }; };
const throttle = (fn, ms) => { let last = 0; return (...a) => { const now = Date.now(); if (now - last >= ms) { last = now; fn(...a); } }; };In short: Unremoved event listeners and timers (setInterval never cleared), references to detached DOM nodes, accidental globals, closures capturing large objects, ever-growing caches/maps, and subscriptions not unsubscribed in SPA components.
- Find them with Chrome DevTools: Performance monitor (JS heap growing), Memory tab heap snapshots compared over time, "Detached elements" panel, and allocation timelines.
- Fix with cleanup on unmount,
WeakMap/WeakReffor metadata, and bounded caches.
In short: Debounce the input (e.g. 300 ms) so the request fires only after typing pauses;
- ignore queries shorter than 2–3 characters;
- cancel the previous in-flight request with
AbortControllerso stale responses don't overwrite newer results (or track the latest request id); - cache recent results;
- show a loading state.
- On the server, add rate limiting and make the search endpoint efficient (indexes, paging).
In short: The main thread is blocked by long synchronous work.
- Move parsing/processing into a Web Worker (postMessage the data or a File/Blob, return results);
- stream the file with
ReadableStream/ process in chunks; - better, process it on the server and fetch paginated or aggregated results;
- virtualise large lists when rendering;
- show progress.
- Measure with the DevTools Performance panel (long tasks).
In short: A typed superset of JavaScript that compiles to plain JavaScript.
- Static types catch errors at compile time, give IntelliSense and safe refactoring, document APIs and scale better for large teams.
- Types are erased at runtime, so there is no runtime cost — and no runtime checking.
In short: Both describe object shapes.
typecan also name unions, intersections, tuples, primitives and mapped/conditional types;interfacesupports declaration merging andextends, and is often preferred for public object contracts.- Interfaces can't express unions; types can't be reopened.
In short: any disables type checking for that value (errors pass silently).
unknownaccepts any value but must be narrowed (typeof/guards/schema) before use — the safe choice for external data andcatcherrors.neverrepresents values that never occur — functions that always throw, or the impossible branch in an exhaustive switch.
In short: A union A | B means the value is one of several types.
- Narrow with
typeof,instanceof,in, equality on a discriminant property (kind), truthiness, or custom type guards (x is T) so TypeScript knows which member you have in each branch.
In short: Type parameters that make code reusable while keeping types: function first<T>(xs: T[]): T | undefined.
- With constraints (
T extends { id: number }) andkeyof(K extends keyof T) you get type-safe helpers such aspluck(items, "name"). - Common built-ins:
Array<T>,Promise<T>,Record<K,V>.
In short: Partial<T> makes all props optional (update/PATCH payloads).
Pick<T,K>keeps chosen keys (summary DTO).Omit<T,K>removes keys (create payload withoutid).Record<K,V>builds an object type with keys K (lookup tables likeRecord<Status, string>).
In short: strict: true enables noImplicitAny, strictNullChecks, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, alwaysStrict, useUnknownInCatchVariables and others.
- It catches the most common bugs — especially null/undefined access.
- New projects should always use it; legacy projects can turn flags on gradually.
In short: A union of object types that share a literal "tag" property (kind, type, status).
- Switching on the tag narrows to the exact member, and a
nevercheck indefaultmakes the switch exhaustive — adding a new member produces compile errors wherever it isn't handled. - Ideal for state (
{ status: "loading" } | { status: "error"; error: string } | { status: "ok"; data: T }) and Redux/NgRx actions.
In short: Types are erased at runtime; http.get<Order[]>() or as Order[] only told the compiler what to expect.
- Prevent: generate client types from the backend OpenAPI document in the build (so contract changes show up as compile errors), validate responses at the boundary with Zod/Valibot, version the API, and add contract/end-to-end tests.
In short: A JavaScript runtime built on Chrome's V8 engine and libuv.
- It runs your JavaScript on a single main thread with an event loop and performs I/O (network, disk) asynchronously, so while one request waits for the database the thread serves other requests.
- Without thread-per-request overhead it handles many concurrent connections efficiently — ideal for APIs, proxies and real-time apps.
In short: The event loop is libuv's loop that repeatedly checks for completed work and runs callbacks on the main thread in phases: timers (setTimeout/setInterval), pending callbacks, poll (I/O events), check (setImmediate), close callbacks.
- Between callbacks Node drains the
process.nextTickqueue and then the promise microtask queue. - Blocking the loop with long synchronous code delays every other request.
In short: Your JavaScript runs on one main thread, but Node itself is not: libuv uses a thread pool (default 4) for file system, DNS lookup, crypto and compression, and the OS handles network I/O asynchronously.
- You can also use worker threads for CPU-heavy JavaScript and the cluster module or multiple containers to use all CPU cores.
In short: CommonJS uses require()/module.exports, loads synchronously at runtime and was Node's original system.
- ES modules use
import/export, are statically analysable (tree-shaking), support top-level await and are the JavaScript standard — enabled with.mjsor"type": "module". - Modern Node supports both and recent versions can
require()ES modules.
In short: A function (req, res, next) in the request pipeline that can read/modify the request and response, end the response, or call next() to pass control on.
- Used for logging, parsing bodies, authentication, CORS, validation and error handling (error middleware has four parameters
(err, req, res, next)). - Middleware runs in the order it's registered.
In short: process.nextTick callbacks run immediately after the current operation, before promise microtasks and before the event loop continues.
setTimeout(fn, 0)runs in the timers phase of a later loop iteration (minimum ~1 ms).setImmediateruns in the check phase right after the poll phase.- Inside an I/O callback, setImmediate always fires before a zero timeout.
- Overusing nextTick can starve I/O.
In short: Use try/catch around await, .catch() on promises, and error-first callbacks for callback APIs.
- In Express 5 rejected async handlers reach the error middleware automatically (Express 4: wrap or use express-async-errors).
- Add a central error middleware, log
unhandledRejectionanduncaughtException(then exit and let the process manager restart), validate input to avoid predictable errors, and never swallow errors silently.
In short: Use the mssql package (tedious driver): create one ConnectionPool at startup with server, database, credentials and encrypt: true, then run parameterised queries with request().input(...).query(...), stored procedures with .execute(), and transactions with sql.Transaction.
- Alternatives: Prisma (
provider = "sqlserver") or Sequelize/TypeORM. - Keep credentials in environment variables or use Entra ID authentication for Azure SQL.
In short: Objects for reading or writing data in chunks (Readable, Writable, Duplex, Transform).
- They keep memory constant for large files or responses, start delivering data before everything is loaded, and handle backpressure.
- Use
stream.pipelineto connect streams with proper error handling — e.g. read a CSV, gzip it, write it to the HTTP response.
In short: Long synchronous operations: big loops, synchronous fs/crypto (readFileSync, pbkdf2Sync), huge JSON.parse/stringify, regex catastrophes, sorting large arrays per request.
- Detect with event-loop lag metrics (
perf_hooks.monitorEventLoopDelay), clinic.js, profiling with--inspect/--prof. - Fix by using async APIs, streaming, chunking work with
setImmediate, moving CPU work to worker threads or another service, and caching results.
In short: The upload is probably read and parsed synchronously in memory (readFileSync, JSON/CSV parse of the whole file) on the main thread, blocking the event loop for all users.
- Stream the upload (multer/busboy to disk or Blob storage), parse it with a streaming CSV parser, insert in batches (bulk insert/TVP), and ideally move the import to a background job/queue with a worker, returning
202 Acceptedand a status endpoint.
In short: React is a library for building UI components.
- Next.js is a React framework that adds file-based routing, server-side and static rendering, Server Components, data fetching and caching, API endpoints (route handlers, Server Actions), image/font optimisation, SEO metadata and production build/deployment tooling.
- It produces full HTML from the server, improving SEO and first load.
In short: Folders inside app/ map to URL segments; a segment becomes routable with page.tsx (UI) or route.ts (API).
layout.tsxwraps child pages and persists across navigation;loading.tsxis the Suspense fallback;error.tsxis the error boundary;not-found.tsxshows 404s.[id]is a dynamic segment,[...slug]catch-all,(group)organises without affecting URLs,_folderis private.
In short: Server Components (default in the App Router) render on the server, can be async and access databases/secrets directly, and send no JavaScript to the browser.
- Client Components (
"use client") are hydrated in the browser and can use state, effects and event handlers. - Use server by default and client only for interactive parts.