I've interviewed Node.js developers for over a decade, first as a candidate working my way up from junior roles, then as a hiring manager building backend teams. The pattern I see repeatedly: developers who know Express routes and can build CRUD apps, but struggle to explain why Node.js works the way it does.
That gap between "can use it" and "understands it" is exactly what backend interviews probe. This guide covers everything you need to cross that gap.
Table of Contents
- Interview Expectations Questions
- Interview Structure Questions
- Event Loop Questions
- Express.js Questions
- API Design Questions
- Database Questions
- Security Questions
- System Design Questions
- Interview Success Questions
- Interview Preparation Questions
- Practice Questions
- Quick Reference
- Related Articles
Interview Expectations Questions
Let's be direct about what companies want when hiring Node.js backend developers:
Technical depth - Not just API knowledge, but understanding of how Node.js handles concurrency, why certain patterns exist, and when to use alternatives.
System thinking - Backend developers must consider scalability, security, and reliability. Can you design systems that don't fall over under load?
Database proficiency - Most backend work involves data. SQL knowledge is non-negotiable, and understanding query performance separates seniors from juniors.
API design sense - REST, GraphQL, or something else? The answer is "it depends," and good candidates know what it depends on.
Security awareness - Backend code is the last line of defense. Interviewers want developers who think about security by default, not as an afterthought.
Interview Structure Questions
Most companies follow a similar progression:
What happens in a phone screen interview?
Phone screens last 30-45 minutes and cover basic JavaScript questions, Node.js fundamentals, and your experience. They're filtering for baseline competency and communication skills.
What happens in a technical phone interview?
Technical phone interviews last about 60 minutes with deeper Node.js questions, often including a coding exercise. Expect questions about async patterns, error handling, and Express middleware.
What is expected in a take-home assignment?
Take-home assignments typically take 2-4 hours to build a small API or backend service. They're evaluating code organization, error handling, testing approach, and whether you follow instructions.
What happens in an on-site interview loop?
On-site loops last 4-6 hours with multiple rounds covering system design, coding, debugging, and behavioral questions. Senior roles have heavier system design components.
Event Loop Questions
If there's one topic that separates Node.js developers who "get it" from those who don't, it's the event loop. This isn't just academic—understanding the event loop helps you write better async code, debug performance issues, and answer interview questions confidently.
Node.js is single-threaded but handles concurrent operations through an event-driven architecture. When you call an async function, Node.js delegates the work (to the OS, thread pool, or elsewhere) and continues executing. When the work completes, a callback is queued for execution.
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
process.nextTick(() => console.log('4'));
console.log('5');
// Output: 1, 5, 4, 3, 2This output surprises many developers. The key is understanding execution phases:
- Synchronous code runs first (1, 5)
process.nextTickcallbacks run before other async callbacks (4)- Microtasks (Promises) run next (3)
- Timers and I/O callbacks run in subsequent phases (2)
Interviewers love this question because it reveals whether you've actually studied Node.js internals or just used it superficially.
Deep dive: JavaScript Event Loop Interview Guide - Comprehensive coverage of async JavaScript execution, with visual explanations and common interview scenarios.
What is the difference between blocking and non-blocking code?
A critical distinction in Node.js: blocking operations freeze the entire process, while non-blocking operations allow other requests to proceed.
// BLOCKING - Never do this in production
const data = fs.readFileSync('/large-file.txt');
processData(data);
// NON-BLOCKING - The Node.js way
fs.readFile('/large-file.txt', (err, data) => {
if (err) throw err;
processData(data);
});
// Other code continues executing hereInterviewers ask about this because blocking code is a common mistake that tanks Node.js performance. One synchronous file read can make your server unresponsive to all other requests.
How do streams and buffers work in Node.js?
For processing large data efficiently, Node.js provides streams. This is a frequent interview topic because it tests understanding of memory management and data flow.
// Memory-efficient file processing
const readStream = fs.createReadStream('large-file.txt');
const writeStream = fs.createWriteStream('output.txt');
readStream.pipe(writeStream);
// With transformation
readStream
.pipe(zlib.createGzip())
.pipe(writeStream);Why streams matter: Reading a 1GB file with readFile loads 1GB into memory. With streams, you process chunks—memory usage stays constant regardless of file size.
Deep dive: Node.js Advanced Interview Guide - Covers event loop internals, streams, worker threads, and advanced async patterns.
Express.js Questions
Express.js dominates Node.js backend development. Even if companies use NestJS, Fastify, or Koa, Express knowledge transfers—and interviewers often ask Express-specific questions regardless.
What is Express middleware and how does it work?
Everything in Express is middleware. Understanding this pattern is essential for answering architecture questions.
// Middleware signature: (req, res, next)
const requestLogger = (req, res, next) => {
console.log(`${req.method} ${req.path}`);
next(); // Pass control to next middleware
};
const authenticate = (req, res, next) => {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
try {
req.user = verifyToken(token);
next();
} catch (error) {
res.status(401).json({ error: 'Invalid token' });
}
};
// Order matters!
app.use(requestLogger); // Runs for all requests
app.use('/api', authenticate); // Runs for /api/* routes
app.use('/api/users', userRoutes);The key insight: middleware executes in order, and each middleware decides whether to pass control forward (next()) or end the request-response cycle.
How do you implement error handling middleware in Express?
Error handling in Express requires a specific pattern—a four-parameter middleware function:
// Error-handling middleware (note the 4 parameters)
const errorHandler = (err, req, res, next) => {
console.error(err.stack);
// Don't leak error details in production
const message = process.env.NODE_ENV === 'production'
? 'Internal server error'
: err.message;
res.status(err.status || 500).json({
error: message,
...(process.env.NODE_ENV !== 'production' && { stack: err.stack })
});
};
// Must be registered last
app.use(errorHandler);A common interview question: "How do you handle errors in async route handlers?" The answer involves wrapping async functions or using a helper:
// Async error wrapper
const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
// Usage
app.get('/users/:id', asyncHandler(async (req, res) => {
const user = await User.findById(req.params.id);
if (!user) {
const error = new Error('User not found');
error.status = 404;
throw error; // Automatically caught and passed to error middleware
}
res.json(user);
}));Deep dive: Express.js Middleware Interview Guide - Complete coverage of middleware patterns, from basics to advanced composition.
When should you use NestJS instead of Express?
For enterprise applications and larger teams, NestJS provides structure that Express leaves to developers. It's built on TypeScript and uses decorators for a more opinionated, Angular-like architecture:
// NestJS controller - decorators define routing
@Controller('users')
export class UsersController {
constructor(private usersService: UsersService) {}
@Get(':id')
@UseGuards(AuthGuard)
async findOne(@Param('id') id: string): Promise<User> {
return this.usersService.findOne(id);
}
@Post()
@UsePipes(ValidationPipe)
async create(@Body() createUserDto: CreateUserDto): Promise<User> {
return this.usersService.create(createUserDto);
}
}When to learn NestJS:
- Enterprise positions often prefer it for maintainability
- Teams with Angular experience find it familiar
- Projects requiring strict architecture and dependency injection
NestJS knowledge is increasingly valuable, but Express proficiency remains the baseline expectation—learn Express first.
Deep dive: NestJS Interview Guide - Modules, dependency injection, guards, pipes, and the request lifecycle.
API Design Questions
Backend interviews inevitably cover API design. You should understand both REST and GraphQL, their trade-offs, and when to choose each.
What are the principles of good REST API design?
REST isn't just "use HTTP methods with JSON." Good REST design follows principles that make APIs intuitive and maintainable:
# Resource-oriented URLs
GET /users # List users
GET /users/123 # Get specific user
POST /users # Create user
PUT /users/123 # Replace user
PATCH /users/123 # Partial update
DELETE /users/123 # Delete user
# Nested resources for relationships
GET /users/123/posts # User's posts
POST /users/123/posts # Create post for user
GET /users/123/posts/456 # Specific post by user
Common interview question: "How do you handle pagination, filtering, and sorting?"
// Query parameters for flexibility
GET /users?page=2&limit=20&sort=-createdAt&status=active
// Implementation
app.get('/users', async (req, res) => {
const { page = 1, limit = 20, sort = '-createdAt', status } = req.query;
const query = status ? { status } : {};
const sortField = sort.startsWith('-') ? sort.slice(1) : sort;
const sortOrder = sort.startsWith('-') ? -1 : 1;
const users = await User.find(query)
.sort({ [sortField]: sortOrder })
.skip((page - 1) * limit)
.limit(parseInt(limit));
const total = await User.countDocuments(query);
res.json({
data: users,
meta: {
page: parseInt(page),
limit: parseInt(limit),
total,
pages: Math.ceil(total / limit)
}
});
});Deep dive: REST API Design Interview Guide - Comprehensive coverage of REST principles, versioning, error responses, and HATEOAS.
When should you use GraphQL instead of REST?
GraphQL solves specific problems REST handles poorly: over-fetching, under-fetching, and multiple round trips for related data.
// REST: Multiple requests needed
GET /users/123
GET /users/123/posts
GET /users/123/followers
// GraphQL: Single request, exact data needed
query {
user(id: "123") {
name
email
posts(limit: 5) {
title
createdAt
}
followersCount
}
}When to choose GraphQL:
- Mobile apps with bandwidth constraints
- Complex, interconnected data
- Multiple clients needing different data shapes
- Rapid frontend iteration
When to stick with REST:
- Simple CRUD operations
- File uploads/downloads
- Caching is critical (REST caches more easily)
- Team unfamiliar with GraphQL
Deep dive: GraphQL Interview Guide - Schema design, resolvers, N+1 problems, and real-world patterns.
Database Questions
"Do I need SQL for a Node.js role?" Yes. Even if the company uses MongoDB, PostgreSQL, or DynamoDB, SQL knowledge demonstrates database fundamentals that transfer everywhere.
What are SQL JOINs and how do they work?
JOIN questions appear in almost every backend interview. Know these cold:
-- INNER JOIN: Only matching records
SELECT users.name, orders.total
FROM users
INNER JOIN orders ON users.id = orders.user_id;
-- LEFT JOIN: All users, even without orders
SELECT users.name, COALESCE(orders.total, 0) as total
FROM users
LEFT JOIN orders ON users.id = orders.user_id;
-- Multiple JOINs: Common in real applications
SELECT
users.name,
orders.id as order_id,
products.name as product_name
FROM users
INNER JOIN orders ON users.id = orders.user_id
INNER JOIN order_items ON orders.id = order_items.order_id
INNER JOIN products ON order_items.product_id = products.id
WHERE users.id = 123;Why interviewers ask this: JOINs reveal whether you understand relational data modeling. Candidates who struggle with JOINs usually struggle with database design.
How do database indexes improve query performance?
Senior backend roles require understanding query performance:
-- Without index: Full table scan (slow)
SELECT * FROM users WHERE email = 'john@example.com';
-- With index: Direct lookup (fast)
CREATE INDEX idx_users_email ON users(email);
-- Composite index for common query patterns
CREATE INDEX idx_orders_user_date ON orders(user_id, created_at);
-- This query uses the composite index efficiently
SELECT * FROM orders
WHERE user_id = 123
AND created_at > '2024-01-01';Common interview question: "How would you optimize a slow query?" The answer involves: checking the execution plan, adding appropriate indexes, and potentially restructuring the query or denormalizing data.
Deep dive: SQL JOINs Interview Guide - Visual explanations of all JOIN types with real-world examples.
When should you use MongoDB or NoSQL databases?
Many Node.js applications use MongoDB. Know when to choose it and how to use it effectively:
// Mongoose schema with validation
const userSchema = new mongoose.Schema({
email: { type: String, required: true, unique: true },
profile: {
name: String,
skills: [String] // Arrays are first-class in MongoDB
}
}, { timestamps: true });
// Aggregation pipeline - MongoDB's powerful query framework
const stats = await Order.aggregate([
{ $match: { status: 'completed' } },
{ $group: { _id: '$userId', total: { $sum: '$amount' } } },
{ $sort: { total: -1 } }
]);When to choose MongoDB over SQL:
- Flexible/evolving schemas
- Document-like data (user profiles, content)
- Horizontal scaling requirements
- Rapid development iteration
When to stick with SQL:
- Complex relationships and JOINs
- ACID transactions across multiple records
- Strict data integrity requirements
Deep dive: MongoDB Interview Guide - Mongoose schemas, aggregation pipelines, and embedding vs referencing.
How do you use PostgreSQL with Node.js?
For relational data, PostgreSQL with the pg library is the Node.js standard:
const { Pool } = require('pg');
const pool = new Pool({ max: 20 });
// Parameterized queries prevent SQL injection
async function getUserOrders(userId) {
const result = await pool.query(
`SELECT o.*, u.name
FROM orders o
JOIN users u ON u.id = o.user_id
WHERE o.user_id = $1
ORDER BY o.created_at DESC`,
[userId]
);
return result.rows;
}Key concepts:
- Connection pooling - Reuse connections instead of creating per-request (~30ms savings)
- Parameterized queries -
$1, $2placeholders prevent SQL injection - Transactions - Get a client, BEGIN, queries, COMMIT/ROLLBACK, release
Deep dive: PostgreSQL & Node.js Interview Guide - Connection pooling, transactions, migrations, and query optimization.
Security Questions
Backend developers are responsible for security. Interviewers test whether you consider security by default or treat it as an afterthought.
What are the OWASP Top 10 security vulnerabilities?
Know the common vulnerabilities and how to prevent them:
Injection (SQL, NoSQL, Command)
// VULNERABLE - SQL injection
const query = `SELECT * FROM users WHERE id = ${req.params.id}`;
// SAFE - Parameterized query
const query = 'SELECT * FROM users WHERE id = $1';
const result = await pool.query(query, [req.params.id]);
// SAFE - ORM with proper escaping
const user = await User.findById(req.params.id);Broken Authentication
// Secure password handling
const bcrypt = require('bcrypt');
// Hashing (during registration)
const hashedPassword = await bcrypt.hash(password, 12);
// Verification (during login)
const isValid = await bcrypt.compare(inputPassword, hashedPassword);
// JWT with appropriate expiration
const token = jwt.sign(
{ userId: user.id },
process.env.JWT_SECRET,
{ expiresIn: '1h' }
);Deep dive: Authentication & JWT Interview Guide - Sessions vs JWT, OAuth 2.0, refresh tokens, and secure auth patterns.
Sensitive Data Exposure
// Never log sensitive data
console.log('User logged in:', { id: user.id }); // Good
console.log('User logged in:', user); // Bad - might log password hash
// Sanitize responses
const sanitizeUser = (user) => ({
id: user.id,
name: user.name,
email: user.email
// Explicitly exclude: password, tokens, internal fields
});Deep dive: Web Security & OWASP Interview Guide - Comprehensive coverage of web security vulnerabilities and defenses.
How do you implement rate limiting for DDoS protection?
Production backends need protection against abuse:
const rateLimit = require('express-rate-limit');
// General rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window
message: 'Too many requests, please try again later'
});
// Stricter limiting for auth endpoints
const authLimiter = rateLimit({
windowMs: 60 * 60 * 1000, // 1 hour
max: 5, // 5 failed attempts per hour
message: 'Too many login attempts'
});
app.use('/api/', limiter);
app.use('/api/auth/', authLimiter);System Design Questions
Senior Node.js roles include system design interviews. Even for mid-level positions, demonstrating architectural thinking sets you apart.
What caching strategies should backend developers know?
const Redis = require('ioredis');
const redis = new Redis();
// Cache-aside pattern
async function getUser(id) {
// Check cache first
const cached = await redis.get(`user:${id}`);
if (cached) {
return JSON.parse(cached);
}
// Cache miss: fetch from database
const user = await User.findById(id);
// Store in cache with expiration
await redis.setex(`user:${id}`, 3600, JSON.stringify(user));
return user;
}
// Cache invalidation on update
async function updateUser(id, data) {
const user = await User.findByIdAndUpdate(id, data, { new: true });
await redis.del(`user:${id}`); // Invalidate cache
return user;
}How do message queues enable async processing?
Not everything needs immediate processing. Message queues decouple components and improve reliability:
// Producer: Queue a job
const Queue = require('bull');
const emailQueue = new Queue('emails', process.env.REDIS_URL);
app.post('/api/users', async (req, res) => {
const user = await User.create(req.body);
// Queue welcome email (don't block the response)
await emailQueue.add('welcome', { userId: user.id });
res.status(201).json(user);
});
// Consumer: Process jobs
emailQueue.process('welcome', async (job) => {
const user = await User.findById(job.data.userId);
await sendWelcomeEmail(user.email);
});How do you scale Node.js applications?
Node.js is single-threaded, but you can scale horizontally:
// cluster.js - Utilize all CPU cores
const cluster = require('cluster');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
console.log(`Master ${process.pid} is running`);
// Fork workers
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker) => {
console.log(`Worker ${worker.process.pid} died, restarting...`);
cluster.fork();
});
} else {
require('./app'); // Your Express app
}Deep dive: System Design Interview Guide - Comprehensive coverage of distributed systems concepts, from caching to microservices.
How do WebSockets enable real-time communication?
Real-time features like chat, notifications, and live updates require bidirectional communication. This is where WebSockets come in:
// Socket.IO server setup
const { Server } = require('socket.io');
const io = new Server(httpServer);
io.on('connection', (socket) => {
// Join a room
socket.on('room:join', (roomId) => {
socket.join(roomId);
socket.to(roomId).emit('user:joined', socket.id);
});
// Broadcast to room
socket.on('message', ({ roomId, text }) => {
io.to(roomId).emit('message', { text, from: socket.id });
});
socket.on('disconnect', () => {
console.log('User disconnected');
});
});Key concepts interviewers ask about:
- Rooms and namespaces for organizing connections
- Authentication during the WebSocket handshake
- Scaling with Redis adapter and sticky sessions
- Handling reconnection and message ordering
Deep dive: WebSockets & Socket.IO Interview Guide - Rooms, namespaces, authentication, and scaling real-time applications.
Interview Success Questions
After conducting hundreds of backend interviews, patterns emerge in what separates successful candidates:
Why is understanding trade-offs important in interviews?
Good candidates don't just know solutions—they know when each solution is appropriate:
- "REST vs GraphQL? Depends on the client requirements and caching needs."
- "SQL vs NoSQL? Depends on data relationships and query patterns."
- "Monolith vs microservices? Start with monolith, extract services when there's clear need."
Why do interviewers want candidates who think about failure?
Backend systems fail. Strong candidates discuss:
- What happens when the database is down?
- How do you handle network timeouts?
- What's your retry strategy?
- How do you prevent cascading failures?
Why should you consider security first in backend development?
Not as an afterthought, but as part of every design decision:
- "We'd need to validate and sanitize that input"
- "That endpoint should be rate-limited"
- "We shouldn't expose internal IDs"
Why is clear communication important for backend developers?
Backend work often involves explaining technical decisions to non-technical stakeholders. Can you explain database indexing to a product manager? Can you justify architectural choices to a skeptical senior engineer?
Interview Preparation Questions
What should you study in weeks 1-2?
Focus on Node.js fundamentals:
- Event loop deep dive (how it actually works, not just the concept)
- Async patterns: callbacks, promises, async/await
- Streams and buffers
- Error handling best practices
What should you study in weeks 3-4?
Focus on Express and API design:
- Middleware composition and patterns
- REST API design principles
- Authentication and authorization
- Input validation and error responses
What should you study in weeks 5-6?
Focus on databases and security:
- SQL fundamentals: JOINs, indexing, transactions
- Query optimization basics
- OWASP Top 10 vulnerabilities
- Authentication patterns (JWT, sessions, OAuth)
What should you study in weeks 7-8?
Focus on system design and practice:
- Caching strategies
- Message queues and async processing
- Scaling patterns
- Mock interviews and coding practice
Practice Questions
Test yourself on these fundamental questions:
Node.js Core:
- Explain the Node.js event loop phases
- What's the difference between
process.nextTickandsetImmediate? - When would you use streams instead of loading data into memory?
Express:
4. How does Express middleware execution order work?
5. How do you handle errors in async route handlers?
6. What's the difference between app.use and app.get?
API Design: 7. How would you version a REST API? 8. When would you choose GraphQL over REST? 9. How do you handle pagination in a REST API?
Databases: 10. Explain the difference between INNER JOIN and LEFT JOIN 11. How does database indexing improve query performance? 12. When would you denormalize data?
Security: 13. How do you prevent SQL injection? 14. What's the purpose of rate limiting? 15. How should passwords be stored?
Quick Reference
| Topic | Key Concepts | Study Resource |
|---|---|---|
| Event Loop | Call stack, phases, microtasks | Event Loop Guide |
| Node.js Core | Streams, buffers, async patterns | Node.js Advanced |
| Express | Middleware, routing, error handling | Express Middleware Guide |
| NestJS | Modules, DI, guards, decorators | NestJS Guide |
| REST API | Resources, methods, status codes | REST API Guide |
| GraphQL | Schema, resolvers, queries | GraphQL Guide |
| SQL | JOINs, indexing, optimization | SQL JOINs Guide |
| MongoDB | Mongoose, aggregation, schema design | MongoDB Guide |
| PostgreSQL | pg library, pooling, transactions | PostgreSQL Guide |
| Authentication | JWT, sessions, OAuth, Passport.js | Auth & JWT Guide |
| Security | OWASP, auth, injection prevention | Web Security Guide |
| System Design | Caching, queues, scaling | System Design Guide |
| WebSockets | Socket.IO, rooms, real-time | WebSockets Guide |
Related Articles
- Complete Technical Interview Career Guide - The full interview process from application to offer
- Complete Frontend Developer Interview Guide - Technical prep for frontend roles
- Complete Java Backend Developer Interview Guide - Technical prep for Java backend roles
- 5+ JavaScript Interview Mistakes 2026 - Avoid common JavaScript interview pitfalls
