Node.js Interview Questions: The Principal Architect Round
50 Highly Advanced, Production-tested Expert Node.js Interview Questions & Answers used by Technical Directors at Top MNCs.
Expert Level (Q1 – Q50)
V8 compiles JavaScript down to native machine code. Since JS is dynamically typed, V8 creates hidden internal classes (Shapes) under the hood to track property offsets. When an object changes its structure dynamically, V8 creates a new hidden class transition tree. Inline Caches store shortcuts for locating property locations within functions.
Event Loop Lag cannot be calculated natively via traditional CPU usage percentages. You must measure the delay between when a timer was scheduled to execute versus when it actually runs. This can be accurately tracked using Node’s native `perf_hooks` module via the `monitorEventLoopDelay` function.
Node’s native `dns.lookup` is a synchronous, blocking system call that utilizes `getaddrinfo()` at the operating system level. Because it is synchronous, libuv is forced to execute it inside the internal thread pool, completely exhausting the default pool of 4 threads and stalling all other file system or crypto tasks.
Backpressure management inside custom streams requires monitoring the return value of `.write()`. If `.write(chunk)` returns `false`, the internal `highWaterMark` threshold has been breached. You must halt reading operations immediately and wait for the destination stream to emit the `’drain’` event before resuming transmission.
To achieve maximum throughput without serialization overhead between workers, you use `SharedArrayBuffer`. It allocates memory outside the main V8 heap that can be shared across multiple threads directly. To avoid race conditions, you use the global `Atomics` object, which provides atomic thread-safe operations and wait/notify sync patterns.
You handle this by leveraging the native `v8` module’s `writeHeapSnapshot()` method inside an automated health monitor loop. You check the memory consumption using `process.memoryUsage().heapUsed` on a regular interval, and trigger a snapshot automatically if usage crosses a set limit.
Under heavy production load, running a debugger causes unacceptable performance penalties. Instead, you boot the application with the `–prof` flag to collect V8 execution samples or run profiling tools like `0x` which utilize system trace mechanisms to capture execution stacks and generate a visual Flame Graph.
`AsyncLocalStorage` relies on internal V8 hooks (`async_hooks`) to trace context continuity across execution contexts and event loops. Every asynchronous boundary crossing (e.g., an await statement) invokes a tracking hook, which incurs a small CPU runtime overhead depending on the nesting complexity.
Prototype Pollution occurs when user input modifies properties on `__proto__`. If an application uses an unvalidated deep-merge utility, an attacker can append global values. If the application later forks a child process using option configurations that fallback on defaults, the attacker can hijack execution arguments via polluted properties.
The Redlock algorithm requires acquiring locks across multiple independent Redis instances concurrently using matching random string validations. To handle clock drift between physical servers, the lock validity time must be adjusted downwards by subtracting a drift factor (a few milliseconds) to guarantee safe mutual exclusion.
Since microservices manage isolated databases, traditional ACID transactions across them are impossible. The Saga pattern designs a workflow as a series of distinct local transactions. Each step has a corresponding reverse “compensating transaction” that executes if any subsequent phase fails, ensuring data consistency.
By default, Node’s global HTTP Agent does not enforce tight configuration constraints under massive production loads. You must instantiate a custom `https.Agent` specifying parameters like `keepAlive: true`, `maxSockets: Infinity`, `maxFreeSockets: 256`, and align the `keepAliveMsecs` parameter to exactly match or fall below the timeout configuration of your downstream load balancer.
ECONNRESET errors when calling downstream internal microservices. The architect discovers the AWS Application Load Balancer has an idle timeout of 60 seconds, while Node’s default agent timeout is shorter. Aligning the connection configurations eliminates the socket drops.Executing synchronous hashing methods (such as `crypto.pbkdf2Sync` or `bcrypt.hashSync`) directly inside a request thread forces the event loop to stop processing everything else for the duration of the calculation. This blocks all other incoming requests from being read or responded to.
A circuit breaker pattern tracks call failures using three operational states: **Closed** (normal traffic), **Open** (failing fast without hitting the degraded dependency), and **Half-Open** (allowing a slow trickle of testing requests to pass through). You implement this pattern using libraries like `opossum` wrapped around outward-facing request closures.
The native `JSON.stringify` operation executes completely synchronously. If an API attempts to serialize a massive 50MB nested relational data object, the main loop locks up entirely while traversing the data structure. You optimize this by using schema-based serialization compilers like `fast-json-stringify`.
To mitigate cache stampede, you use an Asynchronous Request Coalescing layer (often called a Single-Flight pattern). When a cache miss happens, instead of letting all 500 concurrent requests query the database at once, you intercept them and ensure only the first request queries the DB while the other 499 hook into its returned Promise.
The default `highWaterMark` for readable streams is 16KB (and 64KB for writable streams). When streaming massive enterprise files, small buffer thresholds generate thousands of tiny chunk operations, causing high CPU overhead. Bumping this property to larger boundaries (e.g., 1MB or 4MB) optimizes the underlying system reads.
Standard string comparison operators (`===`) break evaluation early the exact millisecond a character mismatch is detected. An attacker can map out the character length of a secret key by measuring tiny nanosecond variations in response times. To solve this, you use fixed-time comparison functions.
Since WebSockets keep an open TCP connection to a specific server instance, Server 1 cannot natively communicate with a client connected to Server 2. You solve this horizontal scale limitation by putting an asynchronous message distribution layer (like Redis Pub/Sub) behind your WebSocket nodes.
You use Native C++ Addons when you need to perform heavy CPU-bound computing (like machine learning models, custom compression algorithms, or image manipulation) that exceeds the performance of the V8 JavaScript interpreter. Node-API provides binary stability across distinct Node versions.
V8 tracks standard JavaScript objects inside its heap layout, which requires regular garbage collection (GC) sweeps. If you store millions of objects in an in-memory cache, the GC pauses will grow longer and degrade performance. `TypedArrays` (like `Int32Array` or `Float64Array`) allocate raw un-managed binary buffers, bypassing V8 GC sweeps entirely.
Replication lag happens when data written to a primary database has not yet synced to a read replica. If a Node service writes a record and then immediately tries to read it from a replica, it will encounter a 404 Not Found anomaly. You solve this by implementing data-routing logic.
Express evaluates routes linearly in the order they are defined. If an application grows to have thousands of routes, matching a request at the bottom of the list requires checking every previous route structure, which incurs an $O(N)$ lookup penalty. You solve this by implementing Radix-Trie based routers like `find-my-way` or switching to Fastify.
Dynamic `import()` expressions allow you to load modules asynchronously at runtime based on variables. This enables you to load isolated tenant code blocks on demand, rather than pre-loading every custom module into memory at server startup.
Slowloris attacks consume server connection limits by opening sockets and sending tiny fragments of HTTP header data very slowly, keeping the connections open as long as possible. You defend against this by tuning the underlying server timeout limits, such as `requestTimeout`, `headersTimeout`, and `keepAliveTimeout` on the HTTP server instance.
When the V8 heap grows large, major garbage collection sweeps can cause “Stop-The-World” pauses that freeze execution for several seconds. You can mitigate this by tuning runtime options via your command configuration, using flags like `–max-old-space-size`, `–optimize-for-size`, and adjusting the incremental marking steps.
Loading millions of database rows directly into a standard array will quickly exhaust your server’s RAM and cause an out-of-memory crash. Instead, you should fetch the data using database streaming cursors and pipe those chunks directly into the HTTP response object.
To guarantee idempotency, every incoming transaction must pass a unique `Idempotency-Key`. Before executing any business logic, Node checks for the existence of this key in Redis using an atomic operation like `SETNX`. If the key already exists, the system bypasses execution and returns the original cached response.
You can identify memory trends programmatically by leveraging the native `v8` module’s heap profiling tools. By taking regular samples of heap allocations over time, you can flag specific object structures that are growing continuously without being cleaned up by the garbage collector.
Command Query Responsibility Segregation (CQRS) separates data modification operations (Commands) from data read operations (Queries). In Node, you can optimize this by using separate database models or entirely separate services for handling writes versus reads.
Because distinct operating system processes cannot share standard in-memory locks, you must implement mutual exclusion at the OS kernel level. This can be achieved by using file descriptor locks via system calls like `flock` or using the `fs-ext` library’s lock functionality.
Mutual TLS requires both the client and the server to validate each other’s X.509 certificates before establishing a connection. In Node, you configure this inside the `https` or `tls` module options by setting `requestCert: true` and `rejectUnauthorized: true`, along with your trusted Certificate Authority (CA) files.
When streaming data to a client over a slow network connection, the server’s writable stream buffer will fill up, causing backpressure. You handle this by listening for the `.write()` method’s `false` return value, pausing the upstream data source, and resuming it only when the socket fires the `’drain’` event.
A reentrancy issue occurs when an asynchronous function is called again before its first invocation has completely finished processing, which can lead to race conditions or unexpected state modifications if the function relies on shared variables.
Serverless functions often suffer from cold start delays while the runtime initializes and compiles code. You can optimize this by utilizing V8 code caching, which stores the compiled machine code of your modules so subsequent invocations can skip the compilation step entirely.
Using the native `vm` module to run untrusted code is unsafe because a malicious script can break out of the context and access the main process. For true isolation, you must execute untrusted code using advanced sandboxing libraries like `isolated-vm`, which runs code within isolated V8 Isolate instances.
Synchronizing data across regions requires an architecture that can handle network latency and potential network partitions. You can coordinate this by using globally distributed databases like Amazon Aurora Global Database or setting up multi-region message replication using systems like Apache Kafka.
Operational errors are predictable runtime failures (such as a database timeout or a invalid user input) that must be caught and handled gracefully. Programmer errors are bugs in the code (such as a `TypeError` or an undefined reference) that put the application into an unstable, unpredictable state.
Forcing garbage collection manually is generally discouraged because it interrupts execution flow. However, in heavy batch processing scripts that load and discard millions of data rows, you can run Node with the `–expose-gc` flag and call the global `gc()` function after processing each batch to free up memory immediately.
Distributed tracing requires passing tracing headers (like `traceparent`) across network requests. OpenTelemetry uses Context Propagation to inject these trace IDs into outgoing HTTP request headers or message broker properties, allowing downstream services to continue the same trace context.
A closure leak happens when a long-lived function or object retains a reference to a parent scope variable that is no longer needed. If that variable holds a large object (like a request context), that memory cannot be cleaned up by the garbage collector as long as the closure remains active.
Manually creating worker threads for individual tasks creates significant performance overhead. Instead, you should use a dedicated thread pool manager like `piscina`. It maintains a stable pool of reusable workers, queueing up incoming tasks and distributing them efficiently across available CPU cores.
HTTP/1.1 limits browsers or client agents to a small number of concurrent TCP connections (typically 6) per domain, meaning requests can get queued behind slow operations. HTTP/2 uses multiplexing to send multiple requests and responses concurrently over a single TCP connection, reducing connection overhead.
A Two-Phase Commit ensures atomic updates across multiple distinct database instances. It operates in two steps: 1) The coordinator service asks all participating databases to **Prepare** and lock the necessary records, and 2) If all databases report success, the coordinator issues a global **Commit** command; otherwise, it aborts the operation.
A sliding window rate limiter tracks timestamps for every request inside a Redis sorted set (`ZSET`). For each incoming request, the application runs a Redis pipeline that removes timestamps older than the current window, counts the remaining elements in the set, and adds the new timestamp if the limit hasn’t been breached.
Standard Redis commands like `GET` and `SET` run sequentially, but if multiple distinct Node processes execute them concurrently, you can still experience race conditions. You can solve this by writing your evaluation logic inside a Lua script. Redis executes Lua scripts atomically, ensuring no other commands can run mid-execution.
Monorepos use package manager workspaces (like npm, yarn, or pnpm workspaces) to manage multiple independent applications alongside shared utility libraries in a single repository. This allows projects to reference local packages directly without publishing them to an external registry.
Instead of restarting servers to pick up configuration changes, your Node.js application can listen for live configuration updates using a centralized system or a message broker channel like Redis Pub/Sub.
Standard V8 heap snapshots only show memory managed by the JavaScript engine; they will not show leaks that occur in raw C++ code. To track down unmanaged memory leaks inside native addons, you must profile the entire Node process using system-level tools like Valgrind, leaks, or TCMalloc.
Using standard `.pipe()` chaining does not forward errors downstream. If an error occurs in the first stream of a chain, it will go unhandled and crash the application. To handle errors safely across a multi-stage pipeline, you should use the native `stream.pipeline()` function, which catches errors at any stage and handles cleanup automatically.
Node.js Interview Questions: The Core Developer Round
50 Intermediate Practical Node.js Questions asked in Top MNCs focusing on Architecture, Performance, and Microservices.
Intermediate Level (Q1 – Q50)
`libuv` is a multi-platform C library that provides support for asynchronous I/O based on event loops. It abstracts non-blocking I/O operations (file system, networking) to provide a consistent interface across different operating systems, and it manages the internal thread pool.
The Event Loop runs in a specific order of phases: 1) Timers (`setTimeout`, `setInterval`), 2) Pending Callbacks (I/O callbacks deferred to the next iteration), 3) Idle/Prepare (internal use), 4) Poll (retrieve new I/O events), 5) Check (`setImmediate`), and 6) Close Callbacks (e.g., `socket.on(‘close’)`).
By default, `libuv` creates a thread pool of 4 threads to handle heavy tasks like DNS lookups, crypto operations, and file I/O. You can alter it by setting `process.env.UV_THREADPOOL_SIZE = ‘x’` (up to 1024) before your app boots.
Backpressure occurs when data is being read from a source faster than it can be written to a destination. The writable stream’s internal buffer fills up, and it must signal the readable stream to pause until the buffer drains to prevent memory exhaustion.
`.pipe()` automatically manages backpressure, but it does not automatically destroy connected streams if an error occurs in the middle of the chain, leading to memory leaks. `pipeline()` (from the `stream` module) safely handles errors and properly cleans up all streams involved.
A graceful shutdown involves listening to OS signals (`SIGINT`, `SIGTERM`), stopping the server from accepting new connections (`server.close()`), completing existing active requests, closing database connections, and finally calling `process.exit(0)`.
The `cluster` module forks the entire Node.js process. Each fork has its own V8 instance, memory, and Event Loop, but shares server ports (great for horizontal scaling). `worker_threads` share the same process memory (via ArrayBuffers) and are designed to offload CPU-heavy mathematical tasks within a single app instance.
You start the Node process with the `–inspect` flag and connect Chrome DevTools. You take a Heap Snapshot, run a load test against your API, and take another snapshot. By comparing the two (Heap Allocation Timeline), you can identify objects (like closures or arrays) that are not being garbage collected.
Callbacks registered with `process.nextTick()` are executed immediately after the current operation, before the Event Loop continues. If you recursively call `nextTick()`, you can cause “I/O Starvation,” where the Event Loop is locked and never reaches the Poll phase to handle incoming requests.
If you repeatedly attach listeners to an EventEmitter (e.g., inside a middleware or request handler) without removing them via `emitter.removeListener()`, the emitter retains a reference to the callback closures, preventing them from being garbage collected.
MaxListenersExceededWarning: Possible EventEmitter memory leak detected.Session cookies are stateful; the server stores a Session ID in memory/Redis and sends a cookie to the client. The server must check the DB on every request. JWT is stateless; the payload (e.g., user role) is cryptographically signed and stored on the client. The server mathematically verifies the signature without querying the database.
Storing JWTs in LocalStorage makes them vulnerable to Cross-Site Scripting (XSS). If an attacker injects malicious JavaScript into your site, that script can easily read `localStorage.getItem(‘token’)` and steal the user’s identity.
Cross-Site Request Forgery (CSRF) is when a malicious site tricks a user’s browser into making an authenticated request to your site (using their active cookies). You protect against it by implementing Anti-CSRF tokens (using packages like `csurf`) or setting the `SameSite` attribute on your cookies.
Rate limiting restricts the number of requests a client (usually identified by IP address) can make in a given timeframe. It is typically implemented using middleware like `express-rate-limit` backed by an in-memory store or Redis for distributed systems.
Redis is an in-memory key-value data store. Because Node is single-threaded, querying a traditional DB (like Postgres) for frequently accessed, unchanging data is slow. Redis acts as a caching layer, returning data in sub-milliseconds.
Opening a new database connection for every HTTP request is incredibly resource-intensive (TCP handshakes, authentication). A connection pool creates a set number of DB connections on startup and reuses them across requests.
You listen to `process.on(‘uncaughtException’)` and `process.on(‘unhandledRejection’)`. However, it is dangerous to keep the server running after an uncaught exception due to unpredictable state. You should log the error to a service (like Sentry), and then synchronously shut down the process (`process.exit(1)`), letting a process manager (PM2/Kubernetes) restart it cleanly.
The built-in `crypto` module provides cryptographic functionality that includes a set of wrappers for OpenSSL’s hash, HMAC, cipher, decipher, sign, and verify functions.
In Express, an error-handling middleware is distinct because it takes exactly four arguments: `(err, req, res, next)`. If you pass an argument to `next(err)` in any normal route, Express will bypass all standard middleware and jump straight to the nearest error handler.
Buffers allocate raw memory *outside* the V8 JavaScript engine heap, meaning they aren’t subject to V8’s memory limits (usually ~1.5GB). `Buffer.alloc()` creates a zero-filled, safe buffer. `Buffer.allocUnsafe()` skips zero-filling, which is faster but may contain old, sensitive data from RAM.
A reverse proxy (like NGINX or HAProxy) sits in front of your Node server. Node is excellent at running application logic but mediocre at handling raw network tasks. The proxy handles SSL termination, gzip compression, serving static assets, and load balancing, freeing Node to focus strictly on API logic.
PM2 is a production process manager for Node.js. It ensures your application stays online 24/7 by automatically restarting it if it crashes. It also provides built-in load balancing (Cluster mode), log management, and zero-downtime reloads.
Microservices often use an Event-Driven Architecture via Message Brokers (like RabbitMQ or Apache Kafka). Instead of Service A making a synchronous HTTP request to Service B, Service A publishes an event to a queue, and Service B consumes it asynchronously.
gRPC is a high-performance RPC framework developed by Google. Unlike REST, which uses JSON over HTTP/1.1, gRPC uses Protocol Buffers (binary data) over HTTP/2. It is strictly typed and vastly faster, making it ideal for internal microservice-to-microservice communication.
An API Gateway is a server that acts as a single entry point into a system of multiple backend microservices. It handles cross-cutting concerns like authentication, routing, rate limiting, and analytics.
Use environment variables. You rely on the `NODE_ENV` variable to dictate the environment. Libraries like `dotenv` load local `.env` files, but in production, variables should be injected by the host (AWS Parameter Store, Kubernetes Secrets, or Docker Compose).
Dependency Injection (DI) is a design pattern where an object receives its dependencies from the outside rather than creating them internally (e.g., using `require`). This decouples code and makes unit testing incredibly easy.
Test-Driven Development (TDD) is writing tests before writing the actual code. In Node, standard tools include Jest or Mocha/Chai for unit and integration testing, and Supertest for testing Express HTTP endpoints.
Jest provides `jest.mock()` to replace the actual implementation of a module with a dummy version. This isolates the function you are testing by preventing side effects like actual HTTP calls or database writes.
By default, `express-session` uses a MemoryStore, which stores session data in the Node process RAM. If the server restarts, all users are logged out. A Persistent Store (like `connect-redis` or `connect-pg-simple`) saves session data externally.
An index is a data structure (usually a B-Tree) that improves the speed of data retrieval operations on a database table. If your Express API queries a table by `email`, applying an index to the `email` column prevents a slow “full table scan”.
A transaction is a sequence of DB operations that must all succeed, or all fail completely (ACID properties). You start a transaction, run queries, and then either `COMMIT` or `ROLLBACK` on error.
Never trust client data. You should validate payloads at the route level before they reach your controllers. Popular libraries include Joi, Zod, or express-validator to enforce data types, lengths, and strict schemas.
Before sending a complex HTTP request (like PUT/DELETE, or containing custom headers), the browser automatically sends an `OPTIONS` request called a preflight. It asks the Node server, “Are you okay with me sending this request?”
SSE is a standard allowing a client to receive automatic updates from a server via an HTTP connection. Unlike WebSockets (which are bi-directional), SSE is uni-directional (Server to Client) and operates over standard HTTP.
HTTP is stateless and strictly Request-Response (client must ask, server answers). WebSockets create a persistent, full-duplex TCP connection. Both the client and server can push messages to each other at any time.
The N+1 problem occurs when you query a list of items (1 query), and then iterate over that list, executing an additional query for each item (N queries). This crushes performance. It is solved using tools like DataLoader (batching) or DB joins.
Bull is a Redis-based queue for Node.js used to handle background jobs. You use it to offload tasks that take a long time and don’t require an immediate HTTP response to the user.
Health monitoring involves exposing a `/health` endpoint, collecting metrics (CPU, Memory, Request latency) using APM tools like Datadog, New Relic, or Prometheus, and setting up alerts for anomaly detection.
When a frontend app needs data from a 3rd party API that does not support CORS, the browser will block the request. You can create a proxy route on your Node backend to fetch the data on behalf of the client (servers don’t enforce CORS on outbound requests) and return it to the frontend.
When you `require()` a module in Node, it is evaluated once, and the result is cached in `require.cache`. Subsequent calls to `require` for the same file will return the exact same cached object reference without re-executing the code.
Content negotiation is an HTTP mechanism where the client tells the server what format of data it expects (using the `Accept` header), and the server (e.g., Express) formats the response accordingly (JSON, XML, HTML).
File uploads are sent using `multipart/form-data`. Express cannot parse this out of the box. You must use middleware like Multer or Busboy to intercept the binary stream, parse the file, and save it to disk or a cloud bucket.
Both methods are highly synchronous and CPU-intensive. Parsing massive JSON payloads (megabytes in size) will block the Event Loop entirely while V8 evaluates the string.
An API endpoint is idempotent if making multiple identical requests has the same effect as making a single request. `GET`, `PUT`, and `DELETE` should be idempotent. `POST` is typically not (it creates a new resource each time).
You use a Node Version Manager like NVM (or Volta) and include a `.nvmrc` file at the root of your project containing the specific Node version (e.g., `v18.17.0`). Additionally, you lock engines in `package.json`.
An ETag (Entity Tag) is an HTTP response header that provides a unique hash representing the requested resource. Express generates this automatically for responses.
The `os` module provides operating system-related utility methods. It allows a Node app to query the underlying server for metrics like total memory, free memory, CPU architecture, and network interfaces.
Prisma is a Next-Generation ORM that replaces traditional class-based models with a custom schema file. It auto-generates a fully type-safe query builder customized exactly for your database, offering vastly superior TypeScript integration and auto-completion compared to older ORMs.
The Thundering Herd problem occurs when a highly trafficked cache key expires, and suddenly hundreds of concurrent requests all hit the primary database simultaneously to rebuild the cache, causing the DB to crash.
Node.js Interview Questions: The Screening Round
50 Essential Beginner Node.js Interview Questions & Answers frequently asked by top MNCs.
Beginner Level (Q1 – Q50)
Node.js is an open-source, cross-platform JavaScript runtime environment built on Chrome’s V8 JavaScript engine. It allows developers to execute JavaScript on the server-side, outside of a web browser.
Browser JS interacts with the DOM (Document Object Model), handles UI events, and has no direct access to the OS. Node.js lacks the DOM but has direct access to the operating system, file system, and network interfaces via built-in modules.
V8 is Google’s open-source high-performance JavaScript and WebAssembly engine, written in C++. It compiles JavaScript directly into native machine code rather than interpreting it in real-time.
Non-blocking I/O means that the Node.js process does not halt (block) when performing Input/Output operations like reading a file or querying a database. It delegates the task and continues executing subsequent code.
Node.js operates on a single main thread using an event loop. However, behind the scenes, it utilizes a C++ library called `libuv` which maintains a thread pool to handle heavy asynchronous I/O tasks.
The Event Loop is the mechanism that allows Node.js to perform non-blocking I/O operations despite being single-threaded. It constantly monitors the call stack and callback queue, pushing resolved asynchronous callbacks onto the stack when it’s empty.
NPM stands for Node Package Manager. It consists of a massive online registry of open-source JavaScript tools/libraries, and a command-line utility used to install and manage these packages in your project.
It is the manifest file of a Node.js project. It holds metadata like the project’s name, version, author, custom scripts, and most importantly, the lists of dependencies (packages) required to run the application.
`dependencies` are packages required for the application to run in production (e.g., Express, Mongoose). `devDependencies` are only needed for local development and testing (e.g., Jest, Nodemon, ESLint).
A callback is a function passed as an argument into another function. In Node.js, callbacks are heavily used to execute code only after an asynchronous operation has completed.
Callback Hell (or the Pyramid of Doom) occurs when multiple asynchronous operations depend on each other, leading to deeply nested, unreadable, and hard-to-maintain callback functions.
A Promise represents the eventual completion (or failure) of an asynchronous operation. Instead of nesting callbacks, Promises allow you to chain `.then()` and `.catch()` blocks, flattening the code structure.
Introduced in ES8, `async/await` is syntactic sugar over Promises. It allows developers to write asynchronous code that visually looks and behaves like synchronous, blocking code, while remaining non-blocking under the hood.
The `fs` (File System) is a core Node.js module used to interact with the file system. It provides methods to read, write, update, delete, and rename files.
`readFile` is asynchronous and non-blocking, requiring a callback. `readFileSync` is synchronous and blocking; it completely halts the Node process until the file is fully read.
The `path` module provides utilities for working with file and directory paths. It is essential because different operating systems use different path separators (Windows uses `\` while Linux/Mac uses `/`).
The `http` module is a core module that allows Node.js to transfer data over the Hyper Text Transfer Protocol (HTTP). It can be used to create an HTTP server that listens to server ports and gives a response back to the client.
The `EventEmitter` class (from the `events` module) is used to trigger and listen to custom events. You use `.on()` to listen for an event and `.emit()` to trigger it.
Streams are objects that let you read data from a source or write data to a destination in continuous, small chunks rather than loading the entire payload into RAM at once.
1) Readable (read data), 2) Writable (write data), 3) Duplex (both read and write), 4) Transform (read, modify, and write).
A Buffer is a temporary storage spot for a chunk of raw binary data. It is primarily used by streams to hold data while it is being transferred from one place to another.
The `process` object provides information about, and control over, the current Node.js process. It is a global object, meaning it is available everywhere without importing.
Express.js is a minimal and flexible Node.js web application framework. It provides a robust set of features for building web and mobile APIs quickly, handling routing, requests, and responses efficiently.
Middleware functions are functions that have access to the request object (`req`), the response object (`res`), and the `next` middleware function in the application’s request-response cycle. They can execute code, make changes to req/res, or end the cycle.
Cross-Origin Resource Sharing (CORS) is a security feature implemented by browsers. By default, a browser won’t let a frontend (e.g., your-react-app.com) make API calls to a backend on a different domain (e.g., your-node-api.com) unless the backend explicitly allows it via CORS headers.
`req.params` contains route parameters (part of the URL path), while `req.query` contains URL query string parameters (everything after the `?` in the URL).
You use the built-in middleware `express.json()`. Without this, if a client sends a JSON payload (like a signup form) via a POST request, `req.body` will be undefined.
`dotenv` is a zero-dependency module that loads environment variables from a `.env` file into `process.env`.
`nodemon` is a development utility that monitors your project for any file changes and automatically restarts your Node.js server.
Express uses methods corresponding to HTTP verbs (e.g., `app.get()`, `app.post()`). For organizing complex apps, Express provides `express.Router()` to create modular, mountable route handlers.
An uncaught exception is a JavaScript error that is thrown but not caught in a `try/catch` block. By default, this immediately crashes the Node.js process.
`Promise.all()` takes an array of promises and runs them concurrently, returning a single Promise that resolves when all of the promises have resolved (or rejects if any one of them fails).
CommonJS uses `require()` and `module.exports` and is loaded synchronously. ES Modules (ESM) use `import` and `export`, are standard across browsers and modern JS, and are loaded asynchronously.
Because Node is single-threaded, running a heavy, blocking synchronous task (like a massive mathematical `while` loop) prevents the Event Loop from processing any new incoming network requests.
`setTimeout(fn, 0)` schedules a script to run after a minimum threshold in ms. `setImmediate(fn)` schedules a script to run immediately after the current phase of the Event Loop (I/O polling) is complete.
`process.nextTick()` schedules a callback function to be executed exactly after the current operation completes, but before the Event Loop moves on to the next phase.
Node interacts with databases using specific driver packages installed via npm. Common choices are `mongoose` for MongoDB, `pg` for PostgreSQL, or ORMs like `Prisma` or `Sequelize`.
JWT is a standard for securely transmitting information between a client and a server as a JSON object. It is most commonly used for stateless user authentication.
`bcrypt` is a library used to securely hash passwords. Hashing is a one-way mathematical function. You cannot “decrypt” a hash back to the original password.
Helmet is a middleware package for Express that automatically secures your application by setting various HTTP response headers.
SemVer is a versioning system used in `package.json` denoted by three numbers: MAJOR.MINOR.PATCH (e.g., `1.4.2`).
While `package.json` specifies acceptable version ranges (e.g., `^1.4.0`), `package-lock.json` records the exact, precise version of every dependency and sub-dependency that was actually installed.
The `cluster` module allows you to easily create child processes (workers) that run simultaneously and share the same server port. This takes advantage of multi-core systems.
Introduced to handle CPU-intensive tasks, the `worker_threads` module allows Node.js to execute JavaScript in parallel using isolated V8 environments, without blocking the main event loop.
Never concatenate raw user input directly into database query strings. Always use parameterized queries (prepared statements) or use an ORM/ODM (like Mongoose or Prisma) which sanitizes inputs automatically.
While `console.log()` is common, professional debugging is done by starting Node with the `–inspect` flag, or by using the built-in debugging tools in an IDE like VS Code to set breakpoints.
Application Programming Interface. In the context of Node, it’s a set of URL endpoints that allow frontend applications (like React, or a mobile app) to communicate with your backend logic and database.
Representational State Transfer (REST) is a software architectural style. A RESTful API relies on standard HTTP methods (GET, POST, PUT, DELETE) linked to specific URLs (resources) in a stateless manner.
By using the `-g` flag (`npm install -g package-name`), the package is installed on your operating system globally, rather than in the local `node_modules` of a specific project. This is usually reserved for CLI tools.
A memory leak occurs when a Node app allocates memory (RAM) but fails to release it back to the OS when it’s no longer needed (garbage collection failure). Over time, RAM fills up and the server crashes.
Angular Interview Questions: Beginner level – The Screening Round
50 Essential Beginner Angular JS Interview Questions & Answers frequently asked by top MNCs.Angular JS interview question, Angular component, TypeScript, Data Binding, Form Control, Routing, HTTP Client
Angular JS interview question, Angular component, TypeScript, Data Binding, Form Control, Routing, HTTP Client
Angular is a development platform and framework built on TypeScript, created and maintained by Google. It is used to build scalable Single-Page Applications (SPAs) and provides a component-based architecture alongside robust tools for routing, state management, and client-server communication.
AngularJS (Angular 1.x) is based on JavaScript and uses an MVC (Model-View-Controller) architecture. Angular (Angular 2+) is a complete rewrite based on TypeScript and uses a Component-based architecture. Modern Angular is faster, highly modular, and provides better support for mobile browsers and object-oriented programming.
TypeScript is a strict syntactical superset of JavaScript that adds optional static typing. Angular uses it because it enables powerful tooling (like robust IDE auto-completion), better refactoring capabilities, and early detection of errors during compile time rather than runtime, making enterprise codebases much easier to maintain.
The core building blocks are:
- Components: Control a patch of the screen called a view.
- Templates: The HTML defining the component’s view.
- Modules (NgModules): Containers for cohesive blocks of code.
- Services: Classes that handle business logic or data fetching.
- Dependency Injection (DI): Mechanism to inject services into components.
A component controls a portion of the screen (a view). It consists of a TypeScript class containing the logic, paired with an HTML template and CSS styles. Components are defined using the @Component decorator.
import { Component } from '@angular/core';
@Component({
selector: 'app-hello',
template: '<h1>Hello {{name}}</h1>'
})
export class HelloComponent {
name = 'Angular';
}Data binding is the process that connects application data (TypeScript class) with the user interface (HTML template). It ensures that changes in the data are reflected in the UI, and user interactions in the UI update the data without requiring manual DOM manipulation.
Angular supports four types of data binding:
- Interpolation:
{{ value }}(Component to DOM) - Property Binding:
[property]="value"(Component to DOM) - Event Binding:
(event)="handler()"(DOM to Component) - Two-Way Binding:
[(ngModel)]="value"(Bi-directional synchronization)
Interpolation is a form of one-way data binding used to embed dynamic string values directly into HTML text nodes or attributes using double curly braces {{ }}.
<!-- Component: title = 'Dashboard' -->
<h1>Welcome to the {{ title }}</h1>Interpolation {{ }} converts the evaluated expression to a string and is generally used for rendering text. Property Binding [ ] sets an element property directly and is required when binding non-string data types (like booleans, objects, or arrays) to DOM properties (e.g., [disabled]="isDisabled").
Event binding allows you to listen to DOM events (like clicks, keystrokes, or mouse movements) and execute a method in the component. It uses parentheses ( ) around the event name.
<button (click)="submitData()">Submit</button>Two-way data binding synchronizes data between the model (component) and the view (UI) in both directions simultaneously. In Angular, this is achieved using the “banana-in-a-box” syntax [(ngModel)]. When the user types in an input, the property updates; when the property changes in code, the input reflects it.
ngModel for two-way binding?To use [(ngModel)], you must import the FormsModule from @angular/forms into your application’s module (or standalone component’s imports array).
Directives are classes that add additional behavior to elements in your Angular applications. They allow you to manipulate the DOM, change the appearance of elements, or create reusable custom behaviors.
The three kinds of directives are:
- Components: Directives with a template (the most common).
- Structural Directives: Change the DOM layout by adding or removing DOM elements (e.g.,
*ngIf,*ngFor). - Attribute Directives: Change the appearance or behavior of an element, component, or another directive (e.g.,
ngClass,ngStyle).
*ngIf with an example.*ngIf is a structural directive that conditionally adds or completely removes an element from the DOM based on whether the expression is truthy or falsy.
<div *ngIf="isLoggedIn">
Welcome back, User!
</div>*ngFor with an example.*ngFor is a structural directive used to loop over an iterable (like an array) and render a template for each item.
<ul>
<li *ngFor="let user of users">{{ user.name }}</li>
</ul>*ngIf and the hidden attribute?*ngIf physically removes the element from the DOM entirely when the condition is false, which saves memory and prevents Angular from checking bindings inside it. The [hidden] attribute keeps the element in the DOM but uses CSS (display: none) to hide it, which can be useful if rendering the element is computationally expensive and toggled frequently.
ngClass?ngClass is an attribute directive that allows you to dynamically add or remove CSS classes on an HTML element based on a boolean expression or state in the component.
<button [ngClass]="{'btn-active': isActive, 'btn-disabled': !isActive}">
Click Me
</button>A Service is a TypeScript class that contains highly cohesive business logic, data fetching mechanisms, or state meant to be shared across multiple components. Services keep components lean, focusing only on the view layer.
Dependency Injection is a core design pattern in Angular where the framework creates and delivers required objects (dependencies like Services) to a class (like a Component) automatically through its constructor, rather than the class instantiating the object itself using the new keyword.
@Injectable() decorator do?The @Injectable() decorator marks a class as participating in the dependency injection system. It tells Angular that this class can be injected as a dependency into other components or services, and that it may also require dependencies to be injected into its own constructor.
providedIn: 'root' mean in a service?When configuring an @Injectable({ providedIn: 'root' }), it registers the service as a singleton at the application root level. This means there is only one instance of the service shared across the entire application, and Angular can tree-shake (remove) the service from the final bundle if it is never actually injected anywhere.
Pipes are simple functions used in HTML templates to transform data for display without altering the original property in the component. They use the pipe character |.
<p>The date is {{ today | date:'shortDate' }}</p>Common built-in pipes include:
DatePipe(formats dates)UpperCasePipe/LowerCasePipe(changes text case)CurrencyPipe(formats numbers as currency)JsonPipe(converts an object to a JSON string for debugging)AsyncPipe(resolves Promises/Observables automatically)
A Pure Pipe executes only when Angular detects a pure change to the input value (like a primitive value change, or a completely new object reference). An Impure Pipe executes on every single component change detection cycle (e.g., every keystroke or mouse move), which can severely impact performance. Pipes are pure by default.
Lifecycle hooks are special methods that allow developers to tap into specific moments in a component’s or directive’s lifecycle. Angular calls these methods when creating, updating, or destroying instances (e.g., ngOnInit, ngOnChanges, ngOnDestroy).
ngOnInit and when is it called?ngOnInit is a lifecycle hook that is called exactly once, immediately after Angular has initialized all data-bound properties (like @Input). It is the standard place to put initialization logic, such as calling a service to fetch data.
ngOnInit instead of the constructor?The constructor is a standard TypeScript feature used primarily for dependency injection. When the constructor runs, Angular has not yet evaluated the component’s @Input() bindings. ngOnInit guarantees that all inputs have been fully resolved, making it the safe place for component initialization logic.
ngOnDestroy used for?ngOnDestroy is called immediately before Angular physically removes the component from the DOM. It is crucial for cleanup tasks, such as unsubscribing from RxJS Observables, detaching event handlers, or clearing intervals to prevent severe memory leaks.
A parent component passes data to a child component using the @Input() decorator on the child’s property, and binding to it via property binding in the parent’s template.
// Child Component
@Input() item: string;
<!-- Parent Template -->
<app-child [item]="parentData"></app-child>A child component sends data back to the parent by emitting custom events using the @Output() decorator combined with an EventEmitter. The parent listens to this event via standard event binding.
// Child Component
@Output() itemSaved = new EventEmitter<string>();
save() { this.itemSaved.emit('Success!'); }
<!-- Parent Template -->
<app-child (itemSaved)="handleSave($event)"></app-child>Angular Routing is a mechanism that allows users to navigate between different views (components) in a Single Page Application (SPA) by updating the browser’s URL without reloading the entire page.
<router-outlet>?The <router-outlet> is a directive from the router library. It acts as a placeholder or dynamic placeholder in your template where the Router physically inserts the component matched by the current active URL route.
routerLink and why is it used instead of href?routerLink is an Angular directive used on anchor tags for navigation. Using a standard HTML href causes the browser to completely reload the page, losing all application state. routerLink intercepts the click, prevents the full reload, and uses the Angular router to load the component seamlessly.
Template-Driven Forms rely heavily on HTML directives (like ngModel, required, minlength) to build the form model and logic directly within the HTML template. They are simple, highly declarative, and suitable for basic forms.
Reactive Forms take a model-driven approach. You define the form’s structure, validation, and logic strictly in the TypeScript component class using FormGroup and FormControl objects. They are robust, highly scalable, and easier to unit test, making them preferred for complex enterprise forms.
FormControl?A FormControl is the fundamental building block of Reactive Forms. It tracks the value and validation status of a single individual form input element (like an email text box or a checkbox).
HttpClient in Angular?HttpClient is an injectable service provided by Angular that performs HTTP requests to external APIs and web servers. It executes asynchronous operations and always returns data wrapped in an RxJS Observable.
An Observable is a feature of the RxJS library representing a continuous stream of data over time. Unlike Promises that resolve only once, an Observable can emit multiple values sequentially and can be cancelled (unsubscribed) at any time.
HttpClient request do nothing until you call .subscribe()?Observables returned by Angular’s HttpClient are “Cold”. This means the execution of the HTTP request is deferred and will not fire until a component or service explicitly subscribes to it using the .subscribe() method.
A Promise emits a single value (or failure), executes immediately upon creation, and cannot be cancelled. An Observable can emit multiple values over time, is lazy (only executes when subscribed to), and can be easily cancelled or retried using RxJS operators.
The async pipe subscribes to an Observable (or Promise) directly in the HTML template and unwraps the emitted value for display. Crucially, it automatically unsubscribes when the component is destroyed, preventing memory leaks.
<ul>
<li *ngFor="let user of users$ | async">{{ user.name }}</li>
</ul>A Route Guard is an interface that tells the Angular router whether it should allow or deny navigation to a requested route. It is primarily used to implement authentication (e.g., preventing an unauthenticated user from accessing a secured dashboard component).
Lazy Loading is a performance optimization technique where feature modules or components are loaded asynchronously on-demand only when the user navigates to their specific route. This dramatically reduces the initial bundle size and speeds up the application’s initial loading time.
The Angular Command Line Interface (CLI) is a powerful terminal tool that allows developers to initialize, develop, scaffold (generate components/services), test, and build Angular applications quickly without writing boilerplate code manually.
Common commands include:
ng new app-name(creates a new app)ng serve(starts a local dev server)ng generate component childorng g c child(scaffolds a component)ng build(compiles the app for production)
angular.json file?The angular.json file is the central workspace configuration file for the Angular CLI. It dictates how the project is built and served, manages environments, and specifies arrays for global CSS stylesheets and external third-party JavaScript scripts to inject.
Content Projection allows a developer to pass HTML content from a parent component into a specified placeholder inside a child component’s template. This is achieved using the <ng-content> tag, creating highly reusable wrapper components (like custom modals or cards).
Introduced in recent versions of Angular, Standalone Components allow developers to build components without declaring them in an NgModule. You set standalone: true in the decorator, and the component manages its own dependencies via an imports array, drastically simplifying architecture.
The environment.ts files define environment-specific variables (like API endpoints or feature toggles) for your application. Angular CLI automatically replaces the default environment.ts with environment.prod.ts when you perform a production build using ng build --configuration production.