1) Implement your own EventEmitter class (on, emit,
off)
// [Link]
class EventEmitter {
constructor() {
[Link] = new Map();
}
on(event, listener) {
if () [Link](event, new Set());
[Link](event).add(listener);
// return unsubscribe
return () => [Link](event, listener);
}
off(event, listener) {
const set = [Link](event);
if (!set) return;
[Link](listener);
if ([Link] === 0) [Link](event);
}
once(event, listener) {
const wrapper = (...args) => {
listener(...args);
[Link](event, wrapper);
};
[Link](event, wrapper);
}
emit(event, ...args) {
const set = [Link](event);
if (!set) return false;
// copy to avoid mutation during iteration
[Link](set).forEach(fn => {
try { fn(...args); } catch (err) { [Link]('Event handler error', err); }
});
return true;
}
}
// Usage example
const ee = new EventEmitter();
const unsub = [Link]('msg', (m) => [Link]('got', m));
[Link]('msg', 'hello'); // got hello
unsub();
[Link]('msg', 'no'); // nothing
[Link] = EventEmitter;
Notes: uses Set to avoid duplicate listeners and returns an unsubscribe function.
2) Function that limits API calls (debounce &
throttle)
// debounce and throttle utilities
function debounce(fn, wait) {
let timer;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => [Link](this, args), wait);
};
}
function throttle(fn, interval) {
let last = 0;
let timer = null;
return function (...args) {
const now = [Link]();
const remaining = interval - (now - last);
if (remaining <= 0) {
if (timer) { clearTimeout(timer); timer = null; }
last = now;
return [Link](this, args);
}
if (!timer) {
timer = setTimeout(() => {
last = [Link]();
timer = null;
[Link](this, args);
}, remaining);
}
};
}
// Example usage
const log = (x) => [Link]('call', x);
const d = debounce(log, 200);
const t = throttle(log, 500);
// Debounce: only last call after 200ms executes
d(1); d(2); d(3);
// Throttle: at most once per 500ms
t(1); t(2); setTimeout(()=>t(3), 600);
Notes: Use debounce for search input; throttle for scroll/resize or frequent events.
3) Implement retry(fn, retries) for async APIs
// [Link]
async function retry(fn, attempts = 3, delayMs = 200) {
let lastErr;
for (let i = 0; i < attempts; i++) {
try {
return await fn();
} catch (err) {
lastErr = err;
if (i < attempts - 1) {
await new Promise(r => setTimeout(r, delayMs * [Link](2, i))); // exponential backoff
}
}
}
throw lastErr;
}
// Example:
const unreliable = async () => {
if ([Link]() < 0.7) throw new Error('fail');
return 'ok';
};
retry(unreliable, 5).then([Link]).catch([Link]);
Notes: Uses exponential backoff; you can add jitter to reduce thundering herd.
4) Create a streaming file reader using [Link]
streams
// [Link]
const fs = require('fs');
const path = require('path');
function streamFile(filePath) {
return new Promise((resolve, reject) => {
const stream = [Link](filePath, { highWaterMark: 64 * 1024 }); // 64KB chunk
[Link]('data', (chunk) => {
// process chunk (e.g., parse, upload chunk, count)
[Link]('chunk size', [Link]);
});
[Link]('end', () => {
[Link]('done');
resolve();
});
[Link]('error', (err) => reject(err));
});
}
// usage
streamFile([Link](__dirname, '[Link]')).catch([Link]);
Notes: Use streams for large files to avoid memory spikes. For uploads to Cloudinary/S3 use
streaming upload APIs.
5) Detect and prevent blocking code in an API
Approach: monitor event-loop lag and offload heavy computations to worker_threads if detected.
// [Link]
const { monitorEventLoopDelay } = require('perf_hooks');
const { Worker } = require('worker_threads');
const h = monitorEventLoopDelay({ resolution: 10 });
[Link]();
setInterval(() => {
const lag = [Link] / 1_000_000; // ms
if (lag > 50) {
[Link](`Event loop lag high: ${[Link](2)}ms`);
}
}, 1000);
// sample offload heavy task:
function runHeavyTask(data) {
return new Promise((resolve, reject) => {
const worker = new Worker('./[Link]', { workerData: data });
[Link]('message', resolve);
[Link]('error', reject);
[Link]('exit', code => {
if (code !== 0) reject(new Error(`Worker stopped with ${code}`));
});
});
}
[Link]:
const { workerData, parentPort } = require('worker_threads');
// heavy computation
let sum = 0;
for (let i = 0; i < 1e8; i++) sum += i;
[Link](sum);
Notes: In production, prefer queueing heavy tasks to separate worker processes (BullMQ /
RabbitMQ).
6) Build a paginated API for users (Express +
Mongoose)
// models/[Link]
const mongoose = require('mongoose');
const UserSchema = new [Link]({
name: String, email: { type: String, index: true }, createdAt: { type: Date, default: [Link] }
});
[Link] = [Link]('User', UserSchema);
// routes/[Link]
const express = require('express');
const router = [Link]();
const User = require('../models/User');
[Link]('/', async (req, res, next) => {
try {
const page = [Link](1, parseInt([Link] || '1', 10));
const limit = [Link](100, parseInt([Link] || '10', 10));
const skip = (page - 1) * limit;
const [items, total] = await [Link]([
[Link]().sort({ createdAt: -1 }).skip(skip).limit(limit).lean(),
[Link]()
]);
[Link]({ page, limit, total, pages: [Link](total / limit), items });
} catch (err) { next(err); }
});
[Link] = router;
Notes: For high-scale use cursor paging (_id or createdAt cursor) to avoid high skip costs. Add
indexes.
7) Create an API rate limiter from scratch (no
library)
// [Link] - token bucket per IP (in-memory)
const buckets = new Map();
function rateLimiter({ tokensPerInterval = 10, intervalMs = 60_000 }) {
return (req, res, next) => {
const key = [Link] || [Link]['x-forwarded-for'] || 'unknown';
const now = [Link]();
let bucket = [Link](key);
if (!bucket) {
bucket = { tokens: tokensPerInterval, last: now };
[Link](key, bucket);
}
// refill
const elapsed = now - [Link];
const refill = [Link](elapsed / intervalMs) * tokensPerInterval;
if (refill > 0) {
[Link] = [Link](tokensPerInterval, [Link] + refill);
[Link] = now;
}
if ([Link] > 0) {
[Link] -= 1;
next();
} else {
[Link](429).json({ error: 'Too many requests' });
}
};
}
[Link] = rateLimiter;
Notes: In-memory solution doesn’t work with multiple instances — use Redis for distributed rate
limiting.
8) Express middleware: validate token, catch errors,
log response time
// middleware/[Link]
const jwt = require('jsonwebtoken');
const SECRET = [Link].JWT_SECRET || 'dev-secret';
function authMiddleware(req, res, next) {
const auth = [Link];
if (!auth) return [Link](401).json({ error: 'Missing auth' });
const token = [Link](' ')[1];
try {
[Link] = [Link](token, SECRET);
next();
} catch (err) {
[Link](401).json({ error: 'Invalid token' });
}
}
[Link] = authMiddleware;
// middleware/[Link]
function responseTimeLogger(req, res, next) {
const start = [Link]();
[Link]('finish', () => {
const end = [Link]();
const ms = Number(end - start) / 1_000_000;
[Link](`${[Link]} ${[Link]} ${[Link]} - ${[Link](2)}ms`);
});
next();
}
// middleware/[Link]
function errorHandler(err, req, res, next) {
[Link](err);
[Link]([Link] || 500).json({ error: [Link] || 'Internal Server Error' });
}
[Link] = { authMiddleware, responseTimeLogger, errorHandler };
Integration:
const { authMiddleware, responseTimeLogger, errorHandler } = require('./middleware');
[Link](responseTimeLogger);
[Link]('/api', someRouter);
[Link](errorHandler);
9) Implement file upload using multer + Cloudinary
// [Link]
const cloudinary = require('cloudinary').v2;
[Link]({
cloud_name: [Link].CLOUDINARY_CLOUD,
api_key: [Link].CLOUDINARY_KEY,
api_secret: [Link].CLOUDINARY_SECRET,
});
// upload route using multer memoryStorage and streaming to cloudinary
const express = require('express');
const multer = require('multer');
const streamifier = require('streamifier');
const router = [Link]();
const upload = multer({ storage: [Link](), limits: { fileSize: 10 * 1024 * 1024 } });
[Link]('/upload', [Link]('file'), async (req, res, next) => {
try {
if (![Link]) return [Link](400).json({ error: 'No file' });
const stream = [Link].upload_stream({ folder: 'uploads' }, (error, result) => {
if (error) return next(error);
[Link]({ url: result.secure_url, public_id: result.public_id });
});
[Link]([Link]).pipe(stream);
} catch (err) { next(err); }
});
[Link] = router;
Notes: For large files use signed direct upload from client to Cloudinary to avoid server memory
usage.
10) Implement versioning in REST API (/v1/users, /
v2/users)
// routers/v1/[Link]
const r1 = require('express').Router();
[Link]('/users', (req, res) => [Link]({ version: 'v1', users: [] }));
[Link] = r1;
// routers/v2/[Link]
const r2 = require('express').Router();
[Link]('/users', (req, res) => [Link]({ version: 'v2', users: [], extras: true }));
[Link] = r2;
// [Link]
[Link]('/api/v1', require('./routers/v1/users'));
[Link]('/api/v2', require('./routers/v2/users'));
Notes: Another approach is header-based versioning or accept-version. Keep backward
compatible where possible.
11) SQL: get top 3 users with most orders
-- PostgreSQL
SELECT user_id, COUNT(*) AS order_count
FROM orders
GROUP BY user_id
ORDER BY order_count DESC
LIMIT 3;
Notes: Add WHERE for time-range (e.g., last month). Make sure orders.user_id is indexed.
12) MongoDB aggregation to group messages by
user and count them
// Mongo shell or Mongoose aggregate
[Link]([
{ $group: { _id: "$userId", messageCount: { $sum: 1 } } },
{ $sort: { messageCount: -1 } }
]);
// With Mongoose
[Link]([
{ $group: { _id: "$userId", messageCount: { $sum: 1 } } },
{ $sort: { messageCount: -1 } }
]).then([Link]);
Notes: For large collections, ensure appropriate indexes used for other filtering stages.
13) Implement soft deletion in PostgreSQL with
deleted_at
Schema change:
ALTER TABLE users ADD COLUMN deleted_at TIMESTAMP NULL;
-- soft delete
UPDATE users SET deleted_at = NOW() WHERE id = $1;
-- "undelete"
UPDATE users SET deleted_at = NULL WHERE id = $1;
Select only non-deleted:
SELECT * FROM users WHERE deleted_at IS NULL;
Suggestion: Use partial index for performance:
CREATE INDEX idx_users_active ON users (id) WHERE deleted_at IS NULL;
Notes: Soft deletes keep audit trail and allow recovery. Consider policies to permanently delete
after retention.
14) Create an index on email and explain its effect
CREATE INDEX idx_users_email ON users (email);
Effect:
Speeds up WHERE email = '...' lookups and unique searches.
Slight overhead on INSERT/UPDATE because index must be maintained.
For unique constraint, use CREATE UNIQUE INDEX or ALTER TABLE ADD CONSTRAINT
UNIQUE.
Mongoose:
const UserSchema = new [Link]({ email: { type: String, index: true } });
15) Write a transaction that transfers money
between two users (Postgres using pg)
// [Link]
const { Pool } = require('pg');
const pool = new Pool();
async function transfer(fromId, toId, amount) {
const client = await [Link]();
try {
await [Link]('BEGIN');
// Lock the rows to avoid race
const fromRes = await [Link]('SELECT balance FROM accounts WHERE user_id=$1 FOR
UPDATE', [fromId]);
const toRes = await [Link]('SELECT balance FROM accounts WHERE user_id=$1 FOR
UPDATE', [toId]);
if (![Link][0] || ![Link][0]) throw new Error('Account not found');
if ([Link][0].balance < amount) throw new Error('Insufficient funds');
await [Link]('UPDATE accounts SET balance = balance - $1 WHERE user_id=$2', [amount,
fromId]);
await [Link]('UPDATE accounts SET balance = balance + $1 WHERE user_id=$2',
[amount, toId]);
// optional: insert ledger rows
await [Link]('COMMIT');
return true;
} catch (err) {
await [Link]('ROLLBACK');
throw err;
} finally {
[Link]();
}
}
Notes: Use FOR UPDATE to serialize concurrent transfers. Use numeric/decimal types for money.
16) Implement a chat room where messages
broadcast only to room members ([Link])
// [Link]
const express = require('express');
const http = require('http');
const { Server } = require('[Link]');
const mongoose = require('mongoose');
const Message = require('./models/Message'); // mongoose model
const app = express();
const server = [Link](app);
const io = new Server(server, { cors: { origin: '*' } });
[Link]('connection', (socket) => {
[Link]('connected', [Link]);
[Link]('joinRoom', async ({ roomId, userId }) => {
[Link](roomId);
[Link] = roomId;
[Link] = userId;
[Link](roomId).emit('userJoined', { userId });
});
[Link]('sendMessage', async ({ roomId, text, userId }) => {
// persist to db
const msg = await [Link]({ roomId, userId, text, createdAt: new Date() });
// send to room
[Link](roomId).emit('newMessage', msg);
});
[Link]('disconnect', () => {
if ([Link]) [Link]([Link]).emit('userLeft', { userId: [Link] });
});
});
[Link](3000);
Mongoose model (Message):
const mongoose = require('mongoose');
const schema = new [Link]({
roomId: String, userId: String, text: String, createdAt: Date
});
[Link] = [Link]('Message', schema);
Notes: For scaling, add Redis adapter to share messages across instances.
17) Build a typing-indicator system with socket
events
// On server (continue from above)
[Link]('typing', ({ roomId, userId }) => {
[Link](roomId).emit('typing', { userId });
});
[Link]('stopTyping', ({ roomId, userId }) => {
[Link](roomId).emit('stopTyping', { userId });
});
Client-side (pseudo):
let typingTimeout;
function userTyping() {
[Link]('typing', { roomId, userId });
clearTimeout(typingTimeout);
typingTimeout = setTimeout(() => {
[Link]('stopTyping', { roomId, userId });
}, 1000); // stop after 1s inactivity
}
Notes: Debounce on client so too many events aren't emitted. Consider presence/last-active
channels.
18) Save chat messages to MongoDB on each event
(Already shown in #16 sendMessage — re-stated with safe write)
// ensure write is not blocking socket
[Link]('sendMessage', async (payload) => {
try {
// persist, but don't block broadcast (fire-and-forget)
[Link]({ ...payload, createdAt: new Date() }).catch([Link]);
[Link]([Link]).emit('newMessage', { ...payload, tempId: [Link] });
} catch (err) {
[Link]('error', { message: 'Failed to send message' });
}
});
Notes: For guaranteed delivery, persist first then broadcast. For low latency, send broadcast then
persist (with monitoring for failures).
19) Implement JWT authentication in Express: login,
verify, protected route
// [Link]
const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const router = [Link]();
const User = require('./models/User');
const SECRET = [Link].JWT_SECRET || 'dev-secret';
[Link]('/login', async (req, res) => {
const { email, password } = [Link];
const user = await [Link]({ email });
if (!user) return [Link](401).json({ error: 'Invalid' });
const ok = await [Link](password, [Link]);
if (!ok) return [Link](401).json({ error: 'Invalid' });
const token = [Link]({ userId: user._id, email: [Link] }, SECRET, { expiresIn: '15m' });
[Link]({ token });
});
// verify middleware
function authenticate(req, res, next) {
const auth = [Link];
if (!auth) return [Link](401).end();
const token = [Link](' ')[1];
try {
[Link] = [Link](token, SECRET);
next();
} catch (err) {
[Link](401).json({ error: 'Invalid token' });
}
}
// protected route
[Link]('/me', authenticate, (req, res) => {
[Link]({ userId: [Link], email: [Link] });
});
[Link] = router;
Notes: Use http-only refresh tokens for renewing access tokens (see #20). Store password
hashes with bcrypt and salted properly.
20) Create a refresh token rotation system
(invalidate old tokens)
Approach: store refresh tokens server-side (DB/Redis) with a rotation scheme: when the client
uses a refresh token, issue a new refresh token and revoke the old one.
// [Link] (simplified)
const express = require('express');
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
const router = [Link]();
const pool = require('./db'); // or Mongoose model RefreshToken
const ACCESS_SECRET = [Link].ACCESS_SECRET || 'access';
const REFRESH_SECRET = [Link].REFRESH_SECRET || 'refresh';
// generate tokens
function genAccess(payload) {
return [Link](payload, ACCESS_SECRET, { expiresIn: '15m' });
}
function genRefresh(payload) {
// create opaque token (safer)
return [Link](64).toString('hex');
}
// store refresh token in DB with userId, expiresAt
// Example table: refresh_tokens(token varchar primary key, user_id int, expires_at timestamptz)
[Link]('/login', async (req, res) => {
// authenticate user (omitted)
const userId = 1;
const access = genAccess({ userId });
const refresh = genRefresh();
await [Link]('INSERT INTO refresh_tokens(token, user_id, expires_at) VALUES($1,$2, NOW()
+ INTERVAL \'30 days\')', [refresh, userId]);
[Link]({ access, refresh });
});
[Link]('/token', async (req, res) => {
const { refresh } = [Link];
// validate token exists
const r = await [Link]('SELECT user_id FROM refresh_tokens WHERE token=$1', [refresh]);
if ([Link] === 0) return [Link](401).json({ error: 'Invalid refresh' });
const userId = [Link][0].user_id;
// rotate: delete old refresh, create new
await [Link]('DELETE FROM refresh_tokens WHERE token=$1', [refresh]);
const newRefresh = genRefresh();
await [Link]('INSERT INTO refresh_tokens(token, user_id, expires_at) VALUES($1,$2, NOW()
+ INTERVAL \'30 days\')', [newRefresh, userId]);
const access = genAccess({ userId });
[Link]({ access, refresh: newRefresh });
});
[Link]('/logout', async (req, res) => {
const { refresh } = [Link];
await [Link]('DELETE FROM refresh_tokens WHERE token=$1', [refresh]);
[Link]({ success: true });
});
[Link] = router;
Notes & security:
Use opaque refresh tokens stored server-side to support revocation.
Use rotate on use approach to prevent stolen token reuse — record token family or device id
to detect reuse.
Secure cookies (HttpOnly, SameSite) are recommended for storing refresh tokens on web
clients.
Set expiration and cleanup old tokens.
Production Considerations / Summary & Next Steps
Testing: add unit & integration tests for all routes and functions.
Scaling: switch in-memory stores (rate limiter, buckets) to Redis for multi-instance
deployments.
Security: always validate inputs (Zod/Joi), set CSP, use helmet, secure cookies, and rotate
keys.
Observability: log request times, errors, and use metrics (Prometheus/Grafana).
Deployment: containerize with Docker and set environment variables via secret stores.