What is the event loop?
The event loop is the core of Node.js. It continuously checks if there are any callbacks to execute. Node.js runs single-threaded, so the event loop ensures non-blocking I/O operations. It processes timers, I/O callbacks, setImmediate, and other async operations in phases.
Explain the difference between process.nextTick() and setImmediate().
process.nextTick() executes callbacks in the microtask queue before the next phase of the event loop. setImmediate() executes in the check phase of the event loop. Microtasks always run before macrotasks, so process.nextTick() always runs before setImmediate(). Example: console.log(1), process.nextTick(() => console.log(2)), setImmediate(() => console.log(3)), console.log(4) outputs 1→4→2→3.
What is middleware in Express.js?
Middleware are functions that have access to the request and response objects. They execute during the lifecycle of a request. Middleware can modify request/response, end the request, or call the next middleware. Express passes control to the next middleware using the next() function. Common middleware: logging, authentication, compression, body parsing.
What are callbacks and callback hell?
Callbacks are functions passed to other functions to be executed later. Callback hell (pyramid of doom) occurs when callbacks are nested too deeply, making code hard to read and maintain. Modern solutions: Promises and async/await. Example of callback hell: fs.readFile(..., (err, data) => { fs.writeFile(..., (err) => { ... }); });
What is the difference between require() and import?
require() is CommonJS (default in Node.js), imports modules synchronously. import is ES Modules, imports asynchronously. require() can be used conditionally, import must be at the top level. CommonJS uses module.exports, ES Modules use export. Node.js supports both through "type": "module" in package.json.
Explain Promises and their states.
Promises have three states: pending (initial), fulfilled (resolved successfully), rejected (failed). Once a promise settles (fulfilled or rejected), it cannot change states. Use .then() for fulfilled, .catch() for rejected, .finally() for both. Promises help avoid callback hell and enable better error handling.
What is async/await and how does it work?
async/await is syntactic sugar for Promises. async makes a function return a Promise. await pauses execution until a Promise resolves, making async code look synchronous. Always use try/catch for error handling. Example: async function getData() { try { const data = await fetchData(); } catch(err) { console.log(err); } }
What is a stream in Node.js?
Streams are objects used to read/write data in chunks rather than loading everything into memory. Four types: Readable, Writable, Duplex, Transform. Streams have methods like .pipe(), .on('data'), .on('end'). Benefits: memory efficiency for large files, improved performance. Example: fs.createReadStream().pipe(fs.createWriteStream()).
What is JWT and how does it work?
JWT (JSON Web Token) is a compact token format for authentication. Structure: Header.Payload.Signature. Header contains algorithm, Payload contains claims/data, Signature verifies integrity. Stateless - server doesn't store tokens. Sent via Authorization header. Advantages: scalable, works well with microservices, supports cross-domain requests.
How does clustering improve performance?
Clustering creates multiple worker processes, one for each CPU core. Master process manages workers. If a worker dies, master can spawn a new one. Distributes load across workers. Improves server reliability and performance. Node.js runs single-threaded, so clustering enables true multi-threading/multi-processing for CPU-intensive tasks.
What is the difference between synchronous and asynchronous code?
Synchronous code blocks execution until complete. Asynchronous code doesn't block, execution continues immediately. Sync: fs.readFileSync() - blocks until file is read. Async: fs.readFile() - continues execution, callback fires when complete. Node.js is designed for async operations. Mixing sync/async can cause performance issues.
How do you handle errors in Node.js?
Methods: try/catch for sync errors, .catch() for Promises, error event listeners, error middleware in Express (4 params). Always handle errors to prevent crashes. Log errors for debugging. Send appropriate HTTP status codes. Use custom error classes. Never ignore errors.
What is CORS and why is it needed?
CORS (Cross-Origin Resource Sharing) allows requests from different origins. By default, browsers block cross-origin requests for security. Configure with Access-Control-Allow-Origin headers. Use middleware: cors() package. Essential for APIs serving multiple domains. Development: allow all. Production: whitelist specific origins.
Explain dependency injection and why it's important.
DI is passing dependencies as arguments rather than creating them inside functions. Benefits: easier testing (mock dependencies), decouples code, improves maintainability. Example: instead of creating DB inside function, pass it as parameter: function getUser(db, id) { return db.find(id); }.
What are memory leaks and how do you prevent them?
Memory leaks occur when memory is no longer used but not freed. Common causes: unclosed timers, unreleased event listeners, unresolved promises, large data retained in closures. Prevention: properly remove listeners, use node --inspect to profile, clear timers with clearTimeout(), use tools like clinic.js to detect leaks.
What is npm and what does package-lock.json do?
npm is Node Package Manager for managing dependencies. package-lock.json locks specific versions for reproducible installs. Ensures all developers use same dependency versions. Prevents "works on my machine" issues. Always commit package-lock.json to version control. package.json specifies version ranges, package-lock.json specifies exact versions.
How do you optimize Node.js applications?
Strategies: clustering, compression (gzip), caching (Redis), load balancing, database query optimization, use streams for large data, minify code, lazy load modules, profile with clinic.js, use CDN, implement rate limiting, connection pooling, avoid synchronous operations, use async/await properly.
What is the buffer in Node.js?
Buffer is a temporary storage for data being moved from one place to another. Fixed-size chunk of memory. Used for reading binary files, network data, crypto operations. Create: Buffer.alloc(size), Buffer.from(string). Convert to string: buffer.toString(). Handle large data efficiently without loading into memory.
Explain the V8 engine and its role in Node.js.
V8 is Chrome's JavaScript engine used by Node.js. Compiles JavaScript to machine code for faster execution. Features: JIT compilation, garbage collection, optimization. Node.js provides bindings between JavaScript and C++ for low-level operations. Understanding V8 helps optimize performance.
What is the purpose of package.json "scripts" section?
Defines custom commands executable via npm run. Example: "start": "node index.js", "dev": "nodemon index.js", "test": "jest", "build": "webpack". Run with npm start or npm run scriptName. Automates common tasks, improves development workflow, standardizes commands across projects.
How do you secure Node.js applications?
Use helmet for security headers, validate/sanitize inputs, use HTTPS, implement authentication/authorization, use environment variables for secrets, keep dependencies updated, implement rate limiting, use prepared statements for DB queries, implement CSRF protection, use strong password hashing (bcrypt), implement logging/monitoring.
What is WebSockets and when would you use it?
WebSockets provide two-way communication between client and server. Unlike HTTP (request-response), WebSockets maintain persistent connection. Use cases: real-time chat, notifications, live updates, collaborative tools, gaming. Libraries: socket.io, ws. Advantages: low latency, persistent connection, supports binary data.