Skip to content

Practical Full Stack with Node.js Interview Questions

41 questions with answers. Hands-on questions where you write code, a query, a formula or concrete steps.

How to approach these questions

Talk while you work: say your approach first, write the simplest correct version, then improve it and mention edge cases.

Solved — practicalStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
3 questions free Unlock the rest in seconds
01

Write a simple custom EventEmitter usage for an order service.

Save to ▾
Show answer
const EventEmitter = require('events'); class OrderService extends EventEmitter { place(order) { // save order... this.emit('placed', order); } } const svc = new OrderService(); svc.on('placed', (o) => console.log('Send email for', o.id)); svc.once('placed', () => console.log('First order today')); svc.on('error', (err) => console.error(err)); svc.place({ id: 101 });
02

Create a basic HTTP server in Node.js without any framework.

Save to ▾
Show answer
const http = require('http'); const server = http.createServer((req, res) => { if (req.method === 'GET' && req.url === '/health') { res.writeHead(200, { 'Content-Type': 'application/json' }); return res.end(JSON.stringify({ status: 'ok' })); } res.writeHead(404); res.end('Not found'); }); server.listen(3000, () => console.log('Listening on 3000'));
03

How do you read a large file efficiently in Node.js?

Save to ▾
Show answer
const fs = require('fs'); const readline = require('readline'); async function countErrors(path) { const rl = readline.createInterface({ input: fs.createReadStream(path), crlfDelay: Infinity }); let count = 0; for await (const line of rl) { if (line.includes('ERROR')) count++; } return count; } // Streams line by line, so a 5 GB log uses little memory.
04

How do you implement graceful shutdown in a Node.js server?

Save to ▾
Show answer
const server = app.listen(3000); async function shutdown(signal) { console.log(signal, 'received, closing'); server.close(async () => { await db.close(); await redis.quit(); process.exit(0); }); setTimeout(() => process.exit(1), 10000).unref(); } process.on('SIGTERM', shutdown); process.on('SIGINT', shutdown); // Stops new connections, finishes in-flight requests, closes pools.
05

What is the output order of this code: console.log('A'); setTimeout(() => console.log('B'), 0); Promise.resolve().then(() => console.log('C')); console.log('D');

Save to ▾
Show answer
The output is A, D, C, B. Synchronous code runs first, printing A and D. Then the microtask queue is drained before the event loop moves to timers, so the promise callback prints C. Finally the timers phase runs the setTimeout callback and prints B. Microtasks always run before the next macrotask such as timers or I/O callbacks.
06

How do you run async tasks in parallel versus in sequence?

Save to ▾
Show answer
// Sequential: each waits for the previous one for (const id of ids) { const user = await getUser(id); results.push(user); } // Parallel: start all, then wait together const users = await Promise.all(ids.map((id) => getUser(id))); // Use sequential only when steps depend on each other // or when you must limit load on a downstream service.
07

How do you limit concurrency when calling an external API for 10,000 records?

Save to ▾
Show answer
async function runLimited(items, limit, worker) { const results = []; let i = 0; async function next() { while (i < items.length) { const idx = i++; results[idx] = await worker(items[idx]); } } await Promise.all(Array.from({ length: limit }, next)); return results; } await runLimited(records, 10, callPartnerApi); // Or use the p-limit package.
08

How do you add a timeout to a promise?

Save to ▾
Show answer
function withTimeout(promise, ms) { let timer; const timeout = new Promise((_, reject) => { timer = setTimeout(() => reject(new Error('Timeout')), ms); }); return Promise.race([promise, timeout]).finally(() => clearTimeout(timer)); } await withTimeout(fetch(url), 3000); // For fetch, AbortSignal.timeout(3000) also cancels the request.
09

Convert this callback-based function to async/await: fs.readFile(path, 'utf8', (err, data) => {...}).

Save to ▾
Show answer
const fs = require('fs/promises'); async function readConfig(path) { try { const data = await fs.readFile(path, 'utf8'); return JSON.parse(data); } catch (err) { if (err.code === 'ENOENT') return {}; throw err; } } // Or wrap any callback API with util.promisify(fn).
10

Write a retry function with exponential backoff for a flaky API call.

Save to ▾
Show answer
async function retry(fn, retries = 3, baseMs = 200) { for (let attempt = 0; ; attempt++) { try { return await fn(); } catch (err) { if (attempt >= retries) throw err; const delay = baseMs * 2 ** attempt + Math.random() * 100; await new Promise((r) => setTimeout(r, delay)); } } } await retry(() => paymentClient.getStatus(id));
11

Write a centralised error handler with a custom AppError class.

Save to ▾
Show answer
class AppError extends Error { constructor(message, status = 500) { super(message); this.status = status; } } app.use((err, req, res, next) => { const status = err.status || 500; logger.error({ err, path: req.path }); res.status(status).json({ error: status === 500 ? 'Internal server error' : err.message }); }); // In a route: throw new AppError('User not found', 404);
12

How do you use express.Router to organise routes?

Save to ▾
Show answer
// routes/users.js const router = require('express').Router(); router.get('/', listUsers); router.get('/:id', getUser); router.post('/', auth, createUser); module.exports = router; // app.js app.use(express.json()); app.use('/api/v1/users', require('./routes/users')); // Keeps each resource in its own module with its own middleware.
13

How do you handle async errors in Express 4 routes?

Save to ▾
Show answer
const asyncHandler = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next); router.get('/:id', asyncHandler(async (req, res) => { const user = await User.findById(req.params.id); if (!user) throw new AppError('Not found', 404); res.json(user); })); // Without this, a rejected promise is never sent to the error handler. // Express 5 does this automatically.
14

Write a simple request logging middleware that records response time.

Save to ▾
Show answer
function requestLogger(req, res, next) { const start = process.hrtime.bigint(); res.on('finish', () => { const ms = Number(process.hrtime.bigint() - start) / 1e6; console.log(`${req.method} ${req.originalUrl} ${res.statusCode} ${ms.toFixed(1)}ms`); }); next(); } app.use(requestLogger); // In production use pino-http or morgan with structured logs.
15

How do you validate request bodies in Express?

Save to ▾
Show answer
const { z } = require('zod'); const createUser = z.object({ name: z.string().min(2), email: z.string().email(), age: z.number().int().min(18).optional() }); const validate = (schema) => (req, res, next) => { const r = schema.safeParse(req.body); if (!r.success) return res.status(400).json({ errors: r.error.issues }); req.body = r.data; next(); }; router.post('/users', validate(createUser), createUserHandler);
16

How do you serve a React build and an API from the same Express server?

Save to ▾
Show answer
const path = require('path'); app.use('/api', apiRouter); app.use(express.static(path.join(__dirname, 'client/dist'), { maxAge: '1y' })); // SPA fallback for client-side routes app.get(/^\/(?!api).*/, (req, res) => { res.sendFile(path.join(__dirname, 'client/dist/index.html')); }); // Register API routes first so they are not caught by the fallback.
17

Implement cursor-based pagination for a products endpoint.

Save to ▾
Show answer
router.get('/products', async (req, res) => { const limit = Math.min(Number(req.query.limit) || 20, 100); const cursor = req.query.cursor; const filter = cursor ? { _id: { $lt: cursor } } : {}; const items = await Product.find(filter) .sort({ _id: -1 }) .limit(limit + 1); const hasMore = items.length > limit; if (hasMore) items.pop(); res.json({ data: items, nextCursor: hasMore ? items[items.length - 1]._id : null }); });
18

How do you implement filtering and sorting safely from query parameters?

Save to ▾
Show answer
const ALLOWED_SORT = ['price', 'createdAt', 'name']; router.get('/products', async (req, res) => { const filter = {}; if (req.query.category) filter.category = String(req.query.category); if (req.query.maxPrice) filter.price = { $lte: Number(req.query.maxPrice) }; const field = ALLOWED_SORT.includes(req.query.sort) ? req.query.sort : 'createdAt'; const dir = req.query.order === 'asc' ? 1 : -1; res.json(await Product.find(filter).sort({ [field]: dir }).limit(50)); }); // Whitelist fields and cast types to block injection.
19

Design endpoints for a simple e-commerce cart and checkout.

Save to ▾
Show answer
GET /api/v1/cart returns the current user's cart. POST /api/v1/cart/items adds an item with productId and quantity. PATCH /api/v1/cart/items/:itemId changes quantity, and DELETE removes it. POST /api/v1/orders creates an order from the cart with an Idempotency-Key header. GET /api/v1/orders/:id shows status, and POST /api/v1/orders/:id/cancel handles cancellation as an action.
20

Write code to verify an HMAC signature on an incoming webhook in Express.

Save to ▾
Show answer
const crypto = require('crypto'); app.post('/webhooks/payment', express.raw({ type: 'application/json' }), (req, res) => { const sig = req.get('X-Signature') || ''; const expected = crypto .createHmac('sha256', process.env.WEBHOOK_SECRET) .update(req.body) .digest('hex'); const ok = sig.length === expected.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)); if (!ok) return res.status(401).end(); handleEvent(JSON.parse(req.body)); res.status(200).end(); });
21

Write JWT authentication middleware for Express.

Save to ▾
Show answer
const jwt = require('jsonwebtoken'); function authenticate(req, res, next) { const header = req.get('Authorization') || ''; const token = header.startsWith('Bearer ') ? header.slice(7) : null; if (!token) return res.status(401).json({ error: 'Missing token' }); try { req.user = jwt.verify(token, process.env.JWT_SECRET, { algorithms: ['HS256'] }); next(); } catch { res.status(401).json({ error: 'Invalid or expired token' }); } }
22

Write signup and login handlers using bcrypt.

Save to ▾
Show answer
const bcrypt = require('bcrypt'); async function signup(req, res) { const hash = await bcrypt.hash(req.body.password, 12); const user = await User.create({ email: req.body.email, password: hash }); res.status(201).json({ id: user.id }); } async function login(req, res) { const user = await User.findOne({ email: req.body.email }).select('+password'); const ok = user && await bcrypt.compare(req.body.password, user.password); if (!ok) return res.status(401).json({ error: 'Invalid credentials' }); res.json({ token: signAccessToken(user) }); }
23

How do you implement rate limiting on a login endpoint?

Save to ▾
Show answer
const rateLimit = require('express-rate-limit'); const { RedisStore } = require('rate-limit-redis'); const loginLimiter = rateLimit({ windowMs: 15 * 60 * 1000, limit: 5, standardHeaders: 'draft-7', legacyHeaders: false, keyGenerator: (req) => `${req.ip}:${req.body.email}`, store: new RedisStore({ sendCommand: (...a) => redis.sendCommand(a) }) }); app.post('/auth/login', loginLimiter, login); // Add account lockout or CAPTCHA after repeated failures.
24

Define a Mongoose schema for a user with validation and an index.

Save to ▾
Show answer
const { Schema, model } = require('mongoose'); const userSchema = new Schema({ name: { type: String, required: true, trim: true }, email: { type: String, required: true, unique: true, lowercase: true }, password: { type: String, required: true, select: false }, role: { type: String, enum: ['user', 'admin'], default: 'user' }, city: String }, { timestamps: true }); userSchema.index({ city: 1, createdAt: -1 }); module.exports = model('User', userSchema);
25

How do you use a MongoDB transaction in Mongoose?

Save to ▾
Show answer
const session = await mongoose.startSession(); try { await session.withTransaction(async () => { await Account.updateOne( { _id: fromId, balance: { $gte: amount } }, { $inc: { balance: -amount } }, { session } ); await Account.updateOne({ _id: toId }, { $inc: { balance: amount } }, { session }); await Transfer.create([{ fromId, toId, amount }], { session }); }); } finally { await session.endSession(); } // Requires a replica set or Atlas cluster.
26

What is the MongoDB aggregation pipeline? Give an example.

Save to ▾
Show answer
// Top 5 cities by revenue in the last 30 days const since = new Date(Date.now() - 30 * 864e5); const result = await Order.aggregate([ { $match: { status: 'paid', createdAt: { $gte: since } } }, { $group: { _id: '$city', revenue: { $sum: '$amount' }, orders: { $sum: 1 } } }, { $sort: { revenue: -1 } }, { $limit: 5 } ]); // Each stage transforms documents; put $match early to use indexes.
27

Write a parameterised query with node-postgres to fetch a user's recent orders.

Save to ▾
Show answer
const { Pool } = require('pg'); const pool = new Pool({ connectionString: process.env.DATABASE_URL, max: 10 }); async function recentOrders(userId, limit = 10) { const { rows } = await pool.query( `SELECT id, amount, status, created_at FROM orders WHERE user_id = $1 ORDER BY created_at DESC LIMIT $2`, [userId, limit] ); return rows; }
28

Implement the cache-aside pattern with Redis for a product lookup.

Save to ▾
Show answer
async function getProduct(id) { const key = `product:${id}`; const cached = await redis.get(key); if (cached) return JSON.parse(cached); const product = await Product.findById(id).lean(); if (product) { await redis.set(key, JSON.stringify(product), { EX: 300 }); } return product; } async function updateProduct(id, data) { await Product.updateOne({ _id: id }, data); await redis.del(`product:${id}`); // invalidate }
29

Write a Jest unit test for a function that calculates order total with GST.

Save to ▾
Show answer
// pricing.js const total = (items, gst = 0.18) => Math.round(items.reduce((s, i) => s + i.price * i.qty, 0) * (1 + gst) * 100) / 100; module.exports = { total }; // pricing.test.js const { total } = require('./pricing'); describe('total', () => { it('adds 18% GST by default', () => { expect(total([{ price: 100, qty: 2 }])).toBe(236); }); it('returns 0 for an empty cart', () => { expect(total([])).toBe(0); }); });
30

How do you test Express routes with Supertest?

Save to ▾
Show answer
const request = require('supertest'); const app = require('../app'); // export app without calling listen describe('GET /api/v1/users/:id', () => { it('returns 404 for an unknown user', async () => { const res = await request(app) .get('/api/v1/users/000000000000000000000000') .set('Authorization', `Bearer ${testToken}`); expect(res.status).toBe(404); expect(res.body.error).toBeDefined(); }); });
31

How do you mock a database or external service in Jest?

Save to ▾
Show answer
// order.service.test.js jest.mock('../repositories/orderRepo'); const repo = require('../repositories/orderRepo'); const { cancelOrder } = require('../services/order.service'); test('cannot cancel a shipped order', async () => { repo.findById.mockResolvedValue({ id: 1, status: 'shipped' }); await expect(cancelOrder(1)).rejects.toThrow('Cannot cancel'); expect(repo.update).not.toHaveBeenCalled(); }); // For HTTP calls, nock or msw can intercept requests.
32

How do you add a correlation ID to every log line for a request?

Save to ▾
Show answer
const { AsyncLocalStorage } = require('async_hooks'); const { randomUUID } = require('crypto'); const als = new AsyncLocalStorage(); app.use((req, res, next) => { const id = req.get('X-Request-Id') || randomUUID(); res.set('X-Request-Id', id); als.run({ requestId: id }, next); }); const log = (msg, extra = {}) => logger.info({ ...extra, requestId: als.getStore()?.requestId }, msg); // Pass the ID to downstream services in headers.
33

How do you find which function is using the most CPU in a Node.js service?

Save to ▾
Show answer
Reproduce the load with autocannon or k6, then profile with node --cpu-prof to generate a .cpuprofile file and open it in Chrome DevTools, or use clinic flame to get a flame graph. Wide bars show hot functions. Common culprits are JSON.stringify on huge objects, synchronous crypto, regex backtracking and heavy loops. Fix and re-measure under the same load.
34

Write a Dockerfile for a production Node.js API.

Save to ▾
Show answer
FROM node:22-alpine AS deps WORKDIR /app COPY package*.json ./ RUN npm ci --omit=dev FROM node:22-alpine ENV NODE_ENV=production WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . USER node EXPOSE 3000 HEALTHCHECK CMD wget -qO- http://localhost:3000/health || exit 1 CMD ["node", "src/server.js"]
35

Write a basic GitHub Actions workflow that tests and builds a Node.js app.

Save to ▾
Show answer
name: ci on: [push, pull_request] jobs: test: runs-on: ubuntu-latest services: mongo: image: mongo:7 ports: ['27017:27017'] steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 22 cache: npm - run: npm ci - run: npm run lint - run: npm test -- --coverage env: MONGO_URL: mongodb://localhost:27017/test
36

How do you use a message queue to process background jobs in Node.js?

Save to ▾
Show answer
const { Queue, Worker } = require('bullmq'); const connection = { host: 'redis', port: 6379 }; const emailQueue = new Queue('email', { connection }); await emailQueue.add('welcome', { userId }, { attempts: 5, backoff: { type: 'exponential', delay: 1000 }, jobId: `welcome:${userId}` }); new Worker('email', async (job) => { await sendWelcomeEmail(job.data.userId); }, { connection, concurrency: 10 });
37

Write a simple Docker Compose file to run a Node API with MongoDB and Redis locally.

Save to ▾
Show answer
services: api: build: . ports: ['3000:3000'] environment: MONGO_URL: mongodb://mongo:27017/app REDIS_URL: redis://redis:6379 depends_on: [mongo, redis] mongo: image: mongo:7 volumes: ['mongo-data:/data/db'] redis: image: redis:7-alpine volumes: mongo-data:
38

How do you manage configuration for dev, staging and production environments?

Save to ▾
Show answer
Follow the twelve-factor approach: keep config in environment variables, not code. Load and validate them at startup with a schema, for example zod or envalid, and fail fast if any are missing. Use .env files only locally, and store production values in the platform's secret store or config maps. Keep the same Docker image across environments and change only configuration.
39

How do you use the Node.js cluster module to use all CPU cores?

Save to ▾
Show answer
const cluster = require('cluster'); const os = require('os'); if (cluster.isPrimary) { for (let i = 0; i < os.availableParallelism(); i++) cluster.fork(); cluster.on('exit', (worker) => { console.log(`Worker ${worker.process.pid} died, restarting`); cluster.fork(); }); } else { require('./server'); // each worker listens on the same port } // In Kubernetes, prefer one process per container and more replicas.
40

How do you prepare a Node.js app for production readiness review?

Save to ▾
Show answer
I check that config is validated, secrets are in a secret store, and logs are structured with request IDs. Health and readiness endpoints exist, graceful shutdown works, timeouts and retries are set on outbound calls, and rate limiting and helmet are enabled. Dashboards, alerts and runbooks are ready, load tests pass targets, backups are verified and a rollback plan is documented.
41

How do you optimise JSON serialisation for very large API responses?

Save to ▾
Show answer
Large JSON.stringify calls block the event loop. Reduce payload size with pagination and field selection first. For unavoidable large outputs, stream the response as NDJSON or a streamed JSON array, use schema-based fast serialisers like fast-json-stringify, which Fastify uses, and cache precomputed responses. Offload heavy transformation to worker threads or background jobs that write files to storage.

Frequently asked questions

Why are practical questions important?

Most technical rounds include at least one live task. Practising the exact typing and reasoning makes you calm and fast in the real round.

How should I answer them?

Talk while you work: say your approach first, write the simplest correct version, then improve it and mention edge cases.