Skip to main content
    Interview Questions

    Node.js Backend Interview 2026: 50+ Event Loop, Async Patterns & Architecture Questions

    Node.js interviews in 2026 separate engineers who can build working apps from those who understand *why* they work. How does the event loop really schedule callbacks? Why is memory management different from browser JavaScript? Can you design a system that handles 10K concurrent connections? These...

    January 4, 2026
    38 min read
    19 views
    Craqly Team
    node.js interview 2026
    event loop questions
    async await interview
    express.js interview
    backend node interview
    node.js performance
    microservices with node
    streams interview
    interview
    interview questions

    Domain Overview

    Node.js is JavaScript running outside the browser, powered by the V8 engine and an event-driven architecture. In 2026, Node.js developers are expected to understand not just Express routing, but the event loop, streams, clustering, and how to build production-ready, scalable applications.

    What makes Node.js interviews challenging is that the language (JavaScript) is familiar, but the runtime environment has unique characteristics. Understanding non-blocking I/O, the event loop phases, and when operations are actually synchronous vs asynchronous is crucial.

    Key Skills Interviewers Look For

    • Event Loop: Understanding phases, microtasks, macrotasks
    • Async Patterns: Callbacks, Promises, async/await, error handling
    • APIs & Frameworks: Express, Fastify, NestJS patterns
    • Streams: Readable, writable, transform, backpressure
    • Performance: Clustering, worker threads, profiling
    • Security: Input validation, authentication, common vulnerabilities
    • Database: ORMs, connection pooling, query optimization
    • Testing: Unit testing, mocking, integration tests

    Fundamental Questions (Q1-Q15)

    1. Explain the Node.js event loop. What are its phases?

    Expert Answer:

    The event loop is what allows Node.js to perform non-blocking I/O despite JavaScript being single-threaded. It has six phases:

    {`   ┌───────────────────────────┐
    ┌─>│           timers          │ ← setTimeout, setInterval
    │  └─────────────┬─────────────┘
    │  ┌─────────────┴─────────────┐
    │  │     pending callbacks     │ ← I/O callbacks deferred
    │  └─────────────┬─────────────┘
    │  ┌─────────────┴─────────────┐
    │  │       idle, prepare       │ ← internal use only
    │  └─────────────┬─────────────┘      ┌───────────────┐
    │  ┌─────────────┴─────────────┐      │   incoming:   │
    │  │           poll            │<─────┤  connections, │
    │  └─────────────┬─────────────┘      │   data, etc.  │
    │  ┌─────────────┴─────────────┐      └───────────────┘
    │  │           check           │ ← setImmediate
    │  └─────────────┬─────────────┘
    │  ┌─────────────┴─────────────┐
    └──┤      close callbacks      │ ← socket.on('close')
       └───────────────────────────┘
    
    Between each phase: process.nextTick() and Promise callbacks (microtasks)`}

    Key insight: process.nextTick() runs before the event loop continues, Promises run in the microtask queue after nextTick, setImmediate runs in the check phase.

    2. What's the difference between process.nextTick() and setImmediate()?

    Expert Answer:

    {`setImmediate(() => console.log('1: setImmediate'));
    process.nextTick(() => console.log('2: nextTick'));
    Promise.resolve().then(() => console.log('3: Promise'));
    console.log('4: sync');
    
    // Output:
    // 4: sync
    // 2: nextTick  (runs before event loop continues)
    // 3: Promise   (microtask queue, after nextTick)
    // 1: setImmediate (check phase of event loop)`}

    When to use:

    • process.nextTick: When you need something to run immediately after current operation, before I/O
    • setImmediate: When you want to break up CPU-heavy work to not block the event loop

    Caution: Recursive process.nextTick() can starve the event loop. Prefer setImmediate for yielding.

    3. How does Node.js handle concurrent requests if JavaScript is single-threaded?

    Expert Answer:

    Node.js uses non-blocking I/O and the event loop to handle concurrency without multiple threads for JavaScript code:

    1. Request comes in: JS thread handles it, sets up I/O operation
    2. I/O delegated: libuv handles it (using thread pool for some operations like file I/O, or OS async primitives for network)
    3. JS thread freed: Can handle other requests while I/O is pending
    4. I/O completes: Callback queued for event loop to pick up
    5. Callback executes: When event loop gets to it

    What's actually threaded:

    • libuv thread pool (default 4 threads) for file I/O, DNS, crypto
    • Worker threads for CPU-intensive JavaScript
    • Child processes for separate Node.js instances

    4. What are streams in Node.js? When would you use them?

    Expert Answer:

    Streams are abstract interfaces for handling data chunk by chunk, rather than loading everything into memory:

    {`// Without streams: loads entire file into memory
    const data = fs.readFileSync('huge-file.csv');
    processData(data); // Memory spike!
    
    // With streams: processes chunks
    const readStream = fs.createReadStream('huge-file.csv');
    readStream.pipe(transformStream).pipe(writeStream);
    // Memory stays low, starts processing immediately`}

    Stream types:

    • Readable: Source of data (fs.createReadStream, http request)
    • Writable: Destination for data (fs.createWriteStream, http response)
    • Duplex: Both readable and writable (TCP socket)
    • Transform: Modify data as it passes through (zlib.createGzip)

    Use streams for: Large file processing, HTTP responses, real-time data, piping between sources

    5. How do you handle errors in async/await code?

    Expert Answer:

    {`// Method 1: try/catch
    async function fetchUser(id) {
      try {
        const user = await db.users.findById(id);
        if (!user) throw new NotFoundError('User not found');
        return user;
      } catch (error) {
        logger.error('Failed to fetch user', { id, error });
        throw error; // Re-throw or handle
      }
    }
    
    // Method 2: Catch at call site
    const user = await fetchUser(id).catch(err => {
      // Handle error
      return null;
    });
    
    // Method 3: Wrapper function for cleaner code
    async function tryCatch(promise) {
      try {
        const data = await promise;
        return [null, data];
      } catch (error) {
        return [error, null];
      }
    }
    
    const [error, user] = await tryCatch(fetchUser(id));
    if (error) { /* handle */ }`}

    Best practice: Use error middleware in Express, custom error classes, and centralized error handling.

    6. Explain middleware in Express.js. How does the middleware chain work?

    Expert Answer:

    Middleware are functions that have access to req, res, and next. They execute in order and can modify the request/response or end the cycle.

    {`// Middleware signature
    function middleware(req, res, next) {
      // Do something
      next(); // Pass to next middleware
      // Or: res.send() to end the cycle
      // Or: next(error) to pass to error handler
    }
    
    // Execution order
    app.use(logger);           // 1. Logs request
    app.use(cors());           // 2. Sets CORS headers
    app.use(authenticate);     // 3. Validates token
    app.use('/api', apiRouter); // 4. Routes
    
    // Error handling middleware (4 params)
    app.use((err, req, res, next) => {
      console.error(err);
      res.status(500).json({ error: 'Internal error' });
    });`}

    7-15. More Fundamental Questions:

    • 7. What is the difference between require() and import?
    • 8. How does Node.js handle uncaught exceptions?
    • 9. Explain the Buffer class. When would you use it?
    • 10. What is the difference between spawn, exec, and fork in child_process?
    • 11. How does module caching work in Node.js?
    • 12. Explain environment variables and dotenv usage.
    • 13. What are EventEmitters? How do you create custom events?
    • 14. How do you read and write files synchronously vs asynchronously?
    • 15. What is the purpose of package-lock.json?

    Intermediate Questions (Q16-Q35)

    16. How do you scale a Node.js application?

    Expert Answer:

    Vertical scaling (single machine):

    • Clustering: Use all CPU cores with cluster module
    • Worker threads: Offload CPU-intensive work
    {`// Cluster example
    const cluster = require('cluster');
    const numCPUs = require('os').cpus().length;
    
    if (cluster.isPrimary) {
      for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
      }
      cluster.on('exit', (worker) => {
        cluster.fork(); // Replace dead workers
      });
    } else {
      app.listen(3000);
    }`}

    Horizontal scaling (multiple machines):

    • Load balancer (nginx, AWS ALB) distributing requests
    • Stateless design (no in-memory sessions)
    • External session store (Redis)
    • Container orchestration (Kubernetes)

    17. How do you handle authentication in a Node.js API?

    Expert Answer:

    {`// JWT Authentication middleware
    const jwt = require('jsonwebtoken');
    
    function authenticate(req, res, next) {
      const authHeader = req.headers.authorization;
      if (!authHeader?.startsWith('Bearer ')) {
        return res.status(401).json({ error: 'No token provided' });
      }
    
      const token = authHeader.split(' ')[1];
      try {
        const payload = jwt.verify(token, process.env.JWT_SECRET);
        req.user = payload;
        next();
      } catch (error) {
        if (error.name === 'TokenExpiredError') {
          return res.status(401).json({ error: 'Token expired' });
        }
        return res.status(401).json({ error: 'Invalid token' });
      }
    }
    
    // Protected route
    app.get('/api/profile', authenticate, (req, res) => {
      res.json({ user: req.user });
    });`}

    Best practices:

    • Short-lived access tokens (15 min)
    • Refresh tokens in httpOnly cookies
    • Password hashing with bcrypt (cost factor 10-12)
    • Rate limiting on auth endpoints

    18. Explain connection pooling with databases.

    Expert Answer:

    Connection pooling maintains a set of open database connections that can be reused, avoiding the overhead of establishing new connections for each query.

    {`// PostgreSQL with pg
    const { Pool } = require('pg');
    
    const pool = new Pool({
      max: 20,              // Maximum connections
      min: 5,               // Minimum connections
      idleTimeoutMillis: 30000,
      connectionTimeoutMillis: 2000,
    });
    
    // Use pool for queries
    async function getUser(id) {
      const client = await pool.connect();
      try {
        const result = await client.query('SELECT * FROM users WHERE id = $1', [id]);
        return result.rows[0];
      } finally {
        client.release(); // Return to pool, don't close!
      }
    }`}

    Why it matters:

    • TCP connection establishment is expensive (~100ms)
    • Database connections consume server resources
    • Pools prevent connection exhaustion under load

    19-35. More Intermediate Questions:

    • 19. How do you implement rate limiting in Node.js?
    • 20. Explain backpressure in streams. How do you handle it?
    • 21. What is the difference between REST and GraphQL in Node.js?
    • 22. How do you implement caching in a Node.js application?
    • 23. Explain worker threads vs child processes.
    • 24. How do you handle file uploads in Node.js?
    • 25. What is CORS and how do you configure it in Express?
    • 26. How do you implement WebSocket communication?
    • 27. Explain the N+1 query problem and how to avoid it.
    • 28. How do you structure a large Node.js application?
    • 29. What is the difference between SQL and NoSQL in Node.js context?
    • 30. How do you implement logging in production?
    • 31. Explain database transactions in Node.js.
    • 32. How do you handle graceful shutdown?
    • 33. What testing strategies do you use for Node.js APIs?
    • 34. How do you implement background jobs/queues?
    • 35. Explain dependency injection in Node.js.

    Advanced & Real-World Questions (Q36-Q50)

    36. Design a high-throughput message queue consumer in Node.js.

    Expert Answer:

    {`const { Worker } = require('bullmq');
    const Redis = require('ioredis');
    
    const connection = new Redis({ maxRetriesPerRequest: null });
    
    const worker = new Worker('email-queue', async (job) => {
      const { to, subject, body } = job.data;
    
      try {
        await sendEmail(to, subject, body);
        return { sent: true };
      } catch (error) {
        // Automatic retry with exponential backoff
        throw error;
      }
    }, {
      connection,
      concurrency: 10,  // Process 10 jobs in parallel
      limiter: {
        max: 100,       // Max 100 jobs
        duration: 1000, // Per second
      },
    });
    
    worker.on('completed', (job, result) => {
      logger.info(\`Job \${job.id} completed\`);
    });
    
    worker.on('failed', (job, err) => {
      logger.error(\`Job \${job.id} failed\`, err);
    });
    
    // Graceful shutdown
    process.on('SIGTERM', async () => {
      await worker.close();
    });`}

    Key considerations:

    • Concurrency based on job type (I/O vs CPU bound)
    • Rate limiting to prevent downstream overload
    • Dead letter queues for failed jobs
    • Idempotency for retry safety

    37-50. More Advanced Questions:

    • 37. How do you debug memory leaks in Node.js?
    • 38. Design a real-time notification system with Node.js
    • 39. How do you implement circuit breakers for external services?
    • 40. Explain how to build a Node.js microservice
    • 41. How do you handle distributed tracing?
    • 42. Design a file processing pipeline with streams
    • 43. How do you implement API versioning?
    • 44. Explain zero-downtime deployments for Node.js
    • 45. How do you secure a Node.js application (OWASP)?
    • 46. Design a multi-tenant SaaS backend
    • 47. How do you implement server-sent events vs WebSockets?
    • 48. Explain Node.js performance profiling techniques
    • 49. How do you handle configuration management across environments?
    • 50. Design an API gateway with Node.js

    Ace Your Node.js Interview

    Practice explaining Node.js concepts and system design with Craqly. Get real-time feedback on your technical explanations.

    Common Mistakes Candidates Make

    ❌ What to Avoid:

    • • Not understanding the event loop phases
    • • Blocking the event loop with CPU-heavy code
    • • Not handling promise rejections
    • • Using sync methods in async code paths
    • • Not closing database connections properly

    ✓ What Works:

    • • Understand async patterns deeply
    • • Use streams for large data
    • • Implement proper error handling
    • • Profile before optimizing
    • • Design for horizontal scaling
    Share this article
    C

    Written by

    Craqly Team

    Comments

    Leave a comment

    No comments yet. Be the first to share your thoughts!

    Ready to Transform Your Interview Skills?

    Join thousands of professionals who have improved their interview performance with AI-powered practice sessions.