Position: Full Stack Developer (2-4 years experience)
Tech Stack: React.js, Node.js, MongoDB, Express.js, HTML & CSS
| Category | Details |
|---|---|
| Experience | 3+ years at Cognizant Technology Solutions |
| Education | B.Tech in CSE (NIET, 8.4 CGPA) |
| Certifications | AWS Certified Developer - Associate, AWS Cloud Practitioner, GitHub Foundations |
| Recognition | Star Performer (February 2025) |
| Key Achievements | 99.9% uptime, 40% faster response times, 87% deployment time reduction |
| Personal Projects | AI Chat Application (Gemini API), Custom AI Persona Platform (OpenAI GPT) |
Answer: React.js is a JavaScript library developed by Facebook for building user interfaces, particularly single-page applications. It uses a component-based architecture and a virtual DOM for efficient updates.
Why choose React:
Example:
// A simple functional component with hooks
import React, { useState, useEffect } from 'react';
const UserProfile = ({ userId }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchUser(userId).then(data => {
setUser(data);
setLoading(false);
});
}, [userId]);
if (loading) return <div>Loading...</div>;
return <div>Welcome, {user.name}</div>;
};
Answer: Traditional class component lifecycle methods are replaced by hooks in functional components:
| Class Lifecycle | Hook Equivalent |
|---|---|
componentDidMount |
useEffect(() => {}, []) |
componentDidUpdate |
useEffect(() => {}, [dependencies]) |
componentWillUnmount |
useEffect(() => { return () => cleanup }, []) |
shouldComponentUpdate |
React.memo() or useMemo() |
Example:
// Class component lifecycle
class MyComponent extends React.Component {
componentDidMount() {
this.subscription = dataSource.subscribe();
}
componentWillUnmount() {
this.subscription.unsubscribe();
}
}
// Equivalent with hooks
const MyComponent = () => {
useEffect(() => {
const subscription = dataSource.subscribe();
// Cleanup function (componentWillUnmount)
return () => subscription.unsubscribe();
}, []); // Empty array = run once on mount
};
Answer: The Virtual DOM is a lightweight JavaScript representation of the actual DOM. When state changes occur, React:
The Diffing Algorithm:
key props to identify list items efficientlyExample demonstrating keys:
// Bad - without keys, React re-renders all items
{items.map(item => <ListItem data={item} />)}
// Good - with unique keys, React can track individual items
{items.map(item => <ListItem key={item.id} data={item} />)}
Answer:
useState: Manages local component state in functional components.
const [count, setCount] = useState(0);
// Update state
setCount(count + 1); // Direct update
setCount(prev => prev + 1); // Functional update (recommended)
useEffect: Handles side effects like data fetching, subscriptions, DOM manipulation.
// Fetching data on component mount
useEffect(() => {
const controller = new AbortController();
const fetchData = async () => {
try {
const response = await fetch('/api/data', {
signal: controller.signal
});
const data = await response.json();
setData(data);
} catch (error) {
if (!error.name === 'AbortError') {
setError(error);
}
}
};
fetchData();
// Cleanup to prevent memory leaks
return () => controller.abort();
}, []); // Empty dependency array = runs once on mount
Answer:
| Solution | Best For | Pros | Cons |
|---|---|---|---|
| useState/useReducer | Local component state | Simple, built-in | Prop drilling in deep trees |
| Context API | Theme, auth, simple global state | Built-in, no extra library | Re-renders all consumers |
| Redux | Complex state, large apps | Predictable, DevTools, middleware | Boilerplate, learning curve |
| Redux Toolkit | Modern Redux apps | Less boilerplate, built-in best practices | Still Redux complexity |
| Zustand/Jotai | Moderate complexity | Minimal API, performant | Smaller ecosystem |
Example with Context API:
// Create context
const ThemeContext = React.createContext('light');
// Provider
const App = () => (
<ThemeContext.Provider value="dark">
<Navbar />
<MainContent />
</ThemeContext.Provider>
);
// Consumer using useContext
const Navbar = () => {
const theme = useContext(ThemeContext);
return <nav className={`navbar-${theme}`}>...</nav>;
};
Example with Redux Toolkit:
// userSlice.js
import { createSlice } from '@reduxjs/toolkit';
const userSlice = createSlice({
name: 'user',
initialState: { profile: null, loading: false },
reducers: {
setUser: (state, action) => {
state.profile = action.payload;
},
setLoading: (state, action) => {
state.loading = action.payload;
}
}
});
export const { setUser, setLoading } = userSlice.actions;
export default userSlice.reducer;
Answer:
Rules of Hooks:
Why these rules exist: React relies on the order of hook calls to correctly associate state with components. Conditional hook calls would break this ordering.
Example - Wrong:
// ❌ BAD - conditional hook
const Component = ({ isLoggedIn }) => {
if (isLoggedIn) {
const [user, setUser] = useState(null); // Hook order changes!
}
};
Example - Correct:
// ✅ GOOD - hook at top level
const Component = ({ isLoggedIn }) => {
const [user, setUser] = useState(null);
if (!isLoggedIn) {
return <LoginPrompt />;
}
return <Dashboard user={user} />;
};
Answer:
React.lazy()Examples:
// React.memo - only re-renders if props change
const ExpensiveComponent = React.memo(({ data }) => {
return <div>{/* expensive rendering */}</div>;
});
// useMemo - caches computed value
const sortedItems = useMemo(() => {
return items.sort((a, b) => a.price - b.price);
}, [items]);
// useCallback - stable function reference
const handleClick = useCallback((id) => {
setSelectedId(id);
}, []);
// Code splitting with React.lazy
const Dashboard = React.lazy(() => import('./Dashboard'));
const App = () => (
<Suspense fallback={<Loading />}>
<Dashboard />
</Suspense>
);
Answer: Node.js is a JavaScript runtime built on Chrome’s V8 engine that allows JavaScript to run server-side. It uses an event-driven, non-blocking I/O model.
Event Loop Phases:
setTimeout() and setInterval() callbackssetImmediate() callbacks ┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ poll │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ check │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │
└───────────────────────────┘
Example demonstrating event loop:
console.log('1 - Start');
setTimeout(() => console.log('2 - Timeout'), 0);
Promise.resolve().then(() => console.log('3 - Promise'));
setImmediate(() => console.log('4 - Immediate'));
console.log('5 - End');
// Output: 1 - Start, 5 - End, 3 - Promise, 2 - Timeout, 4 - Immediate
Answer: Middleware functions have access to the request object (req), response object (res), and the next middleware function. They can:
Types of Middleware:
app.use(), app.METHOD()router.use(), router.METHOD()(err, req, res, next)express.json(), express.static()cors, helmet, morganExample:
const express = require('express');
const app = express();
// Logger middleware
const logger = (req, res, next) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
next();
};
// Authentication middleware
const authenticate = (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
res.status(403).json({ error: 'Invalid token' });
}
};
// Error handling middleware (must have 4 params)
const errorHandler = (err, req, res, next) => {
console.error(err.stack);
res.status(err.status || 500).json({
error: err.message || 'Internal Server Error'
});
};
// Apply middleware
app.use(express.json());
app.use(logger);
app.use('/api/protected', authenticate);
app.use(errorHandler); // Error handler last
Answer: A well-structured application follows separation of concerns:
project/
├── src/
│ ├── config/ # Environment, database config
│ │ ├── db.js
│ │ └── index.js
│ ├── controllers/ # Request handlers
│ │ ├── userController.js
│ │ └── productController.js
│ ├── middleware/ # Custom middleware
│ │ ├── auth.js
│ │ ├── validate.js
│ │ └── errorHandler.js
│ ├── models/ # Database models
│ │ ├── User.js
│ │ └── Product.js
│ ├── routes/ # API routes
│ │ ├── userRoutes.js
│ │ └── productRoutes.js
│ ├── services/ # Business logic
│ │ ├── userService.js
│ │ └── emailService.js
│ ├── utils/ # Helper functions
│ │ ├── logger.js
│ │ └── validators.js
│ └── app.js # Express app setup
├── tests/ # Test files
├── .env # Environment variables
└── server.js # Entry point
Example - Clean Architecture:
// routes/userRoutes.js
const router = express.Router();
const userController = require('../controllers/userController');
const { authenticate } = require('../middleware/auth');
const { validateUser } = require('../middleware/validate');
router.get('/', authenticate, userController.getAll);
router.post('/', validateUser, userController.create);
router.get('/:id', authenticate, userController.getById);
// controllers/userController.js
const userService = require('../services/userService');
exports.getAll = async (req, res, next) => {
try {
const users = await userService.getAllUsers(req.query);
res.json({ success: true, data: users });
} catch (error) {
next(error);
}
};
// services/userService.js
const User = require('../models/User');
exports.getAllUsers = async (filters) => {
const { page = 1, limit = 10, sortBy = 'createdAt' } = filters;
return User.find()
.sort(sortBy)
.skip((page - 1) * limit)
.limit(limit);
};
Answer:
Best Practices:
Example:
// utils/AppError.js
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
// utils/catchAsync.js
const catchAsync = (fn) => {
return (req, res, next) => {
fn(req, res, next).catch(next);
};
};
// Controller using catchAsync
const getUser = catchAsync(async (req, res, next) => {
const user = await User.findById(req.params.id);
if (!user) {
return next(new AppError('User not found', 404));
}
res.json({ success: true, data: user });
});
// Centralized error handler
const errorHandler = (err, req, res, next) => {
err.statusCode = err.statusCode || 500;
if (process.env.NODE_ENV === 'development') {
res.status(err.statusCode).json({
status: err.status,
error: err,
message: err.message,
stack: err.stack
});
} else {
// Production - don't leak error details
if (err.isOperational) {
res.status(err.statusCode).json({
status: err.status,
message: err.message
});
} else {
console.error('ERROR 💥', err);
res.status(500).json({
status: 'error',
message: 'Something went wrong'
});
}
}
};
Answer: Streams are objects that let you read data from a source or write data to a destination in a continuous manner, handling data piece by piece without loading everything into memory.
Types of Streams:
| Type | Description | Example |
|——|————-|———|
| Readable | Read data from source | fs.createReadStream(), HTTP request |
| Writable | Write data to destination | fs.createWriteStream(), HTTP response |
| Duplex | Both read and write | TCP sockets |
| Transform | Modify data while reading/writing | Compression, encryption |
Example - Reading a large file efficiently:
const fs = require('fs');
const zlib = require('zlib');
// Without streams - loads entire file into memory
const data = fs.readFileSync('large-file.txt');
// With streams - processes in chunks
const readStream = fs.createReadStream('large-file.txt');
const writeStream = fs.createWriteStream('output.txt');
const gzip = zlib.createGzip();
// Pipe: readable -> transform -> writable
readStream
.pipe(gzip)
.pipe(writeStream)
.on('finish', () => console.log('File compressed successfully'));
// Stream events
readStream.on('data', (chunk) => {
console.log(`Received ${chunk.length} bytes`);
});
readStream.on('end', () => {
console.log('Finished reading');
});
readStream.on('error', (err) => {
console.error('Error:', err);
});
Creating a custom Transform stream:
const { Transform } = require('stream');
const upperCaseTransform = new Transform({
transform(chunk, encoding, callback) {
this.push(chunk.toString().toUpperCase());
callback();
}
});
process.stdin
.pipe(upperCaseTransform)
.pipe(process.stdout);
Answer: Node.js runs on a single thread, but clustering allows you to create child processes that share the same server port to utilize multiple CPU cores.
Using the cluster module:
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
console.log(`Master ${process.pid} is running`);
// Fork workers for each CPU
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died`);
// Replace the dead worker
cluster.fork();
});
} else {
// Workers share the TCP connection
http.createServer((req, res) => {
res.writeHead(200);
res.end(`Hello from worker ${process.pid}\n`);
}).listen(8000);
console.log(`Worker ${process.pid} started`);
}
Using PM2 (Production Process Manager):
# Start with cluster mode
pm2 start app.js -i max # Uses all CPU cores
pm2 start app.js -i 4 # Uses 4 instances
# Other useful PM2 commands
pm2 list # List all processes
pm2 logs # View logs
pm2 monit # Monitor resources
pm2 restart all # Restart all apps
pm2 reload all # Zero-downtime reload
Scaling strategies diagram:
┌─────────────────┐
│ Load Balancer │
│ (Nginx/AWS) │
└────────┬────────┘
│
┌────────────────────┼────────────────────┐
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Server 1 │ │ Server 2 │ │ Server 3 │
│ ┌───────────┐ │ │ ┌───────────┐ │ │ ┌───────────┐ │
│ │ Worker 1 │ │ │ │ Worker 1 │ │ │ │ Worker 1 │ │
│ │ Worker 2 │ │ │ │ Worker 2 │ │ │ │ Worker 2 │ │
│ │ Worker 3 │ │ │ │ Worker 3 │ │ │ │ Worker 3 │ │
│ │ Worker 4 │ │ │ │ Worker 4 │ │ │ │ Worker 4 │ │
│ └───────────┘ │ │ └───────────┘ │ │ └───────────┘ │
└───────────────┘ └───────────────┘ └───────────────┘
Answer:
| Feature | process.nextTick() |
setImmediate() |
|---|---|---|
| When executed | Before I/O callbacks | After I/O callbacks |
| Priority | Higher (microtask queue) | Lower (macrotask queue) |
| Use case | Critical async operations | Yield to I/O operations |
Example demonstrating the difference:
console.log('1 - Start');
setTimeout(() => console.log('2 - setTimeout'), 0);
setImmediate(() => console.log('3 - setImmediate'));
process.nextTick(() => console.log('4 - nextTick'));
Promise.resolve().then(() => console.log('5 - Promise'));
console.log('6 - End');
// Output:
// 1 - Start
// 6 - End
// 4 - nextTick (microtask - runs first after sync code)
// 5 - Promise (microtask - runs after nextTick)
// 2 - setTimeout (timer phase)
// 3 - setImmediate (check phase)
When to use each:
// Use process.nextTick for:
// 1. Error handling before continuing
function apiCall(callback) {
if (typeof callback !== 'function') {
return process.nextTick(() => {
throw new TypeError('Callback must be a function');
});
}
// ... rest of function
}
// 2. Ensuring async behavior
function asyncOperation(callback) {
process.nextTick(callback); // Always async
}
// Use setImmediate for:
// 1. Breaking up CPU-intensive tasks
function processLargeArray(array, callback) {
let index = 0;
function processChunk() {
const end = Math.min(index + 1000, array.length);
while (index < end) {
// Process item
index++;
}
if (index < array.length) {
setImmediate(processChunk); // Allow I/O between chunks
} else {
callback();
}
}
processChunk();
}
Answer:
1. Use Helmet for HTTP security headers:
const helmet = require('helmet');
app.use(helmet()); // Sets various security headers
// Specific headers
// X-XSS-Protection
// X-Content-Type-Options: nosniff
// X-Frame-Options: DENY
// Content-Security-Policy
2. Rate limiting to prevent brute force:
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window
message: 'Too many requests, please try again later',
standardHeaders: true,
legacyHeaders: false
});
app.use('/api/', limiter);
// Stricter limit for auth routes
const authLimiter = rateLimit({
windowMs: 60 * 60 * 1000, // 1 hour
max: 5, // 5 failed attempts
message: 'Too many login attempts'
});
app.use('/api/auth/login', authLimiter);
3. Input validation and sanitization:
const { body, validationResult } = require('express-validator');
app.post('/user',
body('email').isEmail().normalizeEmail(),
body('password')
.isLength({ min: 8 })
.matches(/^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[@$!%*?&])/),
body('name').trim().escape(),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Process valid input
}
);
4. Prevent NoSQL injection:
// ❌ Vulnerable to injection
app.post('/login', async (req, res) => {
const user = await User.findOne({
email: req.body.email,
password: req.body.password // Can be { $gt: '' }
});
});
// ✅ Safe - use mongo-sanitize
const sanitize = require('mongo-sanitize');
app.post('/login', async (req, res) => {
const email = sanitize(req.body.email);
const password = sanitize(req.body.password);
const user = await User.findOne({ email });
const isValid = await bcrypt.compare(password, user.password);
});
5. Secure environment variables:
// .env file (never commit!)
JWT_SECRET=your-super-secret-key
DB_PASSWORD=database-password
// config.js
require('dotenv').config();
module.exports = {
jwtSecret: process.env.JWT_SECRET,
dbUrl: `mongodb+srv://user:${process.env.DB_PASSWORD}@cluster.mongodb.net`
};
Security checklist:
Answer:
| Feature | CommonJS (CJS) | ES Modules (ESM) |
|---|---|---|
| Syntax | require() / module.exports |
import / export |
| Loading | Synchronous | Asynchronous |
| Hoisting | Not hoisted | Imports are hoisted |
| Default in Node | Yes (legacy) | Needs "type": "module" |
| File extension | .js, .cjs |
.mjs or configure package.json |
CommonJS example:
// math.js (CommonJS)
const add = (a, b) => a + b;
const subtract = (a, b) => a - b;
module.exports = { add, subtract };
// or
module.exports.add = add;
exports.subtract = subtract;
// app.js
const { add, subtract } = require('./math');
const math = require('./math');
console.log(math.add(2, 3));
ES Modules example:
// math.mjs (ES Modules)
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
export default { add, subtract };
// app.mjs
import { add, subtract } from './math.mjs';
import math from './math.mjs';
console.log(add(2, 3));
// Dynamic import (works in both)
const module = await import('./math.mjs');
package.json configuration:
{
"name": "my-app",
"type": "module", // Enable ES Modules for .js files
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
}
}
Key differences in behavior:
// CommonJS - this is valid
if (condition) {
const module = require('./module'); // Dynamic require
}
// ESM - imports must be at top level
import { something } from './module'; // Must be at top
// ESM - use dynamic import() for conditional
if (condition) {
const module = await import('./module');
}
Answer:
MongoDB stores data in flexible, JSON-like documents (BSON). Design decisions:
| Approach | When to Use | Pros | Cons |
|---|---|---|---|
| Embedded | One-to-few, data accessed together | Faster reads, atomic writes | Document size limit (16MB), data duplication |
| Referenced | One-to-many, many-to-many | Normalized, flexible | Requires multiple queries or $lookup |
Example - Embedded (Good for blog posts with comments):
// Embedded document schema
const blogPostSchema = new Schema({
title: String,
content: String,
author: {
name: String,
email: String,
avatar: String
},
comments: [{
user: String,
text: String,
createdAt: { type: Date, default: Date.now }
}]
});
Example - Referenced (Good for users with multiple orders):
// User schema
const userSchema = new Schema({
name: String,
email: String
});
// Order schema with reference
const orderSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true
},
products: [{
product: { type: Schema.Types.ObjectId, ref: 'Product' },
quantity: Number
}],
total: Number
});
// Populate references
const orders = await Order.find()
.populate('user', 'name email')
.populate('products.product', 'name price');
Answer: Indexes improve query performance by allowing MongoDB to find documents without scanning every document.
Types of Indexes:
Example:
const userSchema = new Schema({
email: {
type: String,
unique: true, // Creates unique index
index: true // Creates regular index
},
firstName: String,
lastName: String,
location: {
type: { type: String },
coordinates: [Number]
},
createdAt: { type: Date, default: Date.now }
});
// Compound index
userSchema.index({ firstName: 1, lastName: 1 });
// Text index for search
userSchema.index({ firstName: 'text', lastName: 'text' });
// Geospatial index
userSchema.index({ location: '2dsphere' });
// Using explain() to analyze query
const result = await User.find({ email: 'test@example.com' })
.explain('executionStats');
console.log(result.executionStats.executionTimeMillis);
Answer: The aggregation pipeline processes documents through stages, each transforming the data.
Common Stages:
$match - Filter documents$group - Group and aggregate$project - Shape output$sort - Order results$lookup - Join collections$unwind - Deconstruct arraysExample - Sales Analytics:
// Get monthly sales report with top products
const salesReport = await Order.aggregate([
// Stage 1: Filter orders from last 6 months
{
$match: {
createdAt: { $gte: new Date(Date.now() - 180 * 24 * 60 * 60 * 1000) },
status: 'completed'
}
},
// Stage 2: Unwind products array
{ $unwind: '$products' },
// Stage 3: Lookup product details
{
$lookup: {
from: 'products',
localField: 'products.productId',
foreignField: '_id',
as: 'productDetails'
}
},
// Stage 4: Group by month and product
{
$group: {
_id: {
month: { $month: '$createdAt' },
year: { $year: '$createdAt' },
productId: '$products.productId'
},
totalQuantity: { $sum: '$products.quantity' },
totalRevenue: { $sum: '$products.subtotal' },
productName: { $first: { $arrayElemAt: ['$productDetails.name', 0] } }
}
},
// Stage 5: Sort by revenue
{ $sort: { totalRevenue: -1 } },
// Stage 6: Limit to top 10
{ $limit: 10 },
// Stage 7: Project final shape
{
$project: {
_id: 0,
month: '$_id.month',
year: '$_id.year',
productName: 1,
totalQuantity: 1,
totalRevenue: { $round: ['$totalRevenue', 2] }
}
}
]);
Answer: MongoDB supports multi-document ACID transactions (since v4.0 for replica sets, v4.2 for sharded clusters).
Example:
const session = await mongoose.startSession();
try {
session.startTransaction();
// Transfer money between accounts
const fromAccount = await Account.findByIdAndUpdate(
fromAccountId,
{ $inc: { balance: -amount } },
{ session, new: true }
);
if (fromAccount.balance < 0) {
throw new Error('Insufficient funds');
}
await Account.findByIdAndUpdate(
toAccountId,
{ $inc: { balance: amount } },
{ session }
);
// Create transaction record
await Transaction.create([{
from: fromAccountId,
to: toAccountId,
amount,
type: 'transfer'
}], { session });
await session.commitTransaction();
console.log('Transaction successful');
} catch (error) {
await session.abortTransaction();
console.error('Transaction failed:', error);
throw error;
} finally {
session.endSession();
}
Answer:
Types of NoSQL databases: | Type | Examples | Best For | |——|———-|———-| | Document | MongoDB, CouchDB | Flexible schemas, JSON-like data | | Key-Value | Redis, DynamoDB | Caching, sessions, simple lookups | | Column-Family | Cassandra, HBase | Time-series, analytics at scale | | Graph | Neo4j, Amazon Neptune | Relationships, social networks |
Why choose MongoDB:
Example use case:
// E-commerce product with varying attributes
const product1 = {
name: "iPhone 15",
category: "electronics",
specs: { ram: "8GB", storage: "256GB", color: "black" }
};
const product2 = {
name: "Nike Air Max",
category: "footwear",
specs: { size: "10", material: "mesh", sole: "air cushion" }
};
// Both can be stored in the same collection!
Answer:
// CREATE
// Insert one document
const result = await User.create({
name: 'Aditya',
email: 'aditya@example.com',
age: 25
});
// Insert many documents
await User.insertMany([
{ name: 'User1', email: 'user1@example.com' },
{ name: 'User2', email: 'user2@example.com' }
]);
// READ
// Find one
const user = await User.findOne({ email: 'aditya@example.com' });
// Find by ID
const user = await User.findById('507f1f77bcf86cd799439011');
// Find all with conditions
const users = await User.find({ age: { $gte: 18 } })
.select('name email') // Projection
.sort({ name: 1 }) // Sort ascending
.skip(0) // Pagination
.limit(10);
// UPDATE
// Update one document
await User.updateOne(
{ email: 'aditya@example.com' },
{ $set: { age: 26 }, $inc: { loginCount: 1 } }
);
// Find and update (returns updated document)
const updated = await User.findByIdAndUpdate(
userId,
{ $push: { orders: newOrderId } },
{ new: true, runValidators: true }
);
// Update many
await User.updateMany(
{ isActive: false },
{ $set: { status: 'inactive' } }
);
// DELETE
// Delete one
await User.deleteOne({ email: 'user@example.com' });
// Delete many
await User.deleteMany({ createdAt: { $lt: oneYearAgo } });
// Find and delete (returns deleted document)
const deleted = await User.findByIdAndDelete(userId);
Answer: Sharding is MongoDB’s method for horizontal scaling - distributing data across multiple machines.
When to use sharding:
Key Concepts:
┌─────────────────────────────────────────────────────────┐
│ mongos (Router) │
│ Routes queries to appropriate shards │
└─────────────────────────────────────────────────────────┘
│
┌────────────────┼────────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Shard 1 │ │ Shard 2 │ │ Shard 3 │
│ (A - G) │ │ (H - N) │ │ (O - Z) │
└──────────┘ └──────────┘ └──────────┘
Shard Key strategies:
| Strategy | Example | Pros | Cons |
|---|---|---|---|
| Range | { timestamp: 1 } |
Good for range queries | Hotspots possible |
| Hashed | { _id: 'hashed' } |
Even distribution | No range queries |
| Zone | Geographic regions | Data locality | More complex |
Example:
// Enable sharding on database
sh.enableSharding("myDatabase");
// Shard a collection with hashed key
sh.shardCollection("myDatabase.users", { _id: "hashed" });
// Shard with range key
sh.shardCollection("myDatabase.orders", { userId: 1, orderDate: 1 });
Answer: Replication provides redundancy and high availability by maintaining multiple copies of data across servers.
Replica Set Architecture:
┌─────────────────────────────────────────────────────────┐
│ Primary Node │
│ • Receives all write operations │
│ • Records changes in oplog │
└─────────────────────────────────────────────────────────┘
│ Replicates to
┌──────────┴──────────┐
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Secondary 1 │ │ Secondary 2 │
│ • Read replicas │ │ • Read replicas │
│ • Can become │ │ • Can become │
│ primary │ │ primary │
└─────────────────┘ └─────────────────┘
Key Benefits:
Mongoose connection with replica set:
mongoose.connect('mongodb://server1:27017,server2:27017,server3:27017/mydb', {
replicaSet: 'myReplicaSet',
readPreference: 'secondaryPreferred', // Read from secondary when possible
w: 'majority', // Write concern
wtimeoutMS: 5000
});
// Read preference options
// primary - Default, read from primary
// primaryPreferred - Primary if available, else secondary
// secondary - Only secondaries
// secondaryPreferred - Secondary if available, else primary
// nearest - Lowest network latency
Answer:
Comparison Operators:
// $eq - Equal
await User.find({ age: { $eq: 25 } });
// $ne - Not equal
await User.find({ status: { $ne: 'inactive' } });
// $gt, $gte - Greater than (or equal)
await User.find({ age: { $gt: 18 } });
// $lt, $lte - Less than (or equal)
await Order.find({ total: { $lte: 1000 } });
// $in - Match any value in array
await User.find({ role: { $in: ['admin', 'moderator'] } });
// $nin - Not in array
await User.find({ status: { $nin: ['banned', 'suspended'] } });
Logical Operators:
// $and - All conditions must match
await User.find({
$and: [
{ age: { $gte: 18 } },
{ status: 'active' }
]
});
// $or - At least one condition must match
await Product.find({
$or: [
{ category: 'electronics' },
{ price: { $lt: 100 } }
]
});
// $not - Negates an expression
await User.find({
age: { $not: { $gt: 65 } }
});
// $nor - None of the conditions match
await User.find({
$nor: [
{ status: 'banned' },
{ isDeleted: true }
]
});
Array Operators:
// $all - Array contains all specified elements
await Product.find({ tags: { $all: ['sale', 'featured'] } });
// $elemMatch - At least one element matches all conditions
await Order.find({
items: {
$elemMatch: {
product: 'iPhone',
quantity: { $gte: 2 }
}
}
});
// $size - Array of specific length
await User.find({ skills: { $size: 5 } });
Update Operators:
// $set - Set field value
await User.updateOne({ _id: userId }, { $set: { name: 'New Name' } });
// $unset - Remove field
await User.updateOne({ _id: userId }, { $unset: { tempField: '' } });
// $inc - Increment value
await Product.updateOne({ _id: productId }, { $inc: { stock: -1, sales: 1 } });
// $push - Add to array
await User.updateOne({ _id: userId }, { $push: { orders: newOrderId } });
// $pull - Remove from array
await User.updateOne({ _id: userId }, { $pull: { cart: { productId: pId } } });
// $addToSet - Add to array if not exists
await User.updateOne({ _id: userId }, { $addToSet: { tags: 'premium' } });
Answer:
Using explain() to analyze queries:
// Get execution stats
const explanation = await User.find({ email: 'test@example.com' })
.explain('executionStats');
console.log(explanation.executionStats);
// {
// executionTimeMillis: 2,
// totalDocsExamined: 1, // Documents scanned
// totalKeysExamined: 1, // Index entries scanned
// nReturned: 1 // Documents returned
// }
// Key metrics to watch:
// - IXSCAN = Using index (good)
// - COLLSCAN = Collection scan (needs index)
// - totalDocsExamined should be close to nReturned
Common optimization techniques:
// 1. Create appropriate indexes
userSchema.index({ email: 1 }); // Single field
userSchema.index({ lastName: 1, firstName: 1 }); // Compound index
userSchema.index({ '$**': 'text' }); // Text index
// 2. Use projection to limit returned fields
const users = await User.find({}, { name: 1, email: 1 }); // Only name and email
// 3. Use lean() for read-only operations
const users = await User.find().lean(); // Returns plain JS objects
// 4. Pagination with skip and limit
const page = 2, limit = 20;
const users = await User.find()
.skip((page - 1) * limit)
.limit(limit);
// 5. Use hint() to force specific index
const users = await User.find({ status: 'active' })
.hint({ status: 1, createdAt: -1 });
// 6. Covered queries (only indexed fields returned)
await User.find(
{ email: 'test@example.com' },
{ email: 1, _id: 0 }
).hint({ email: 1 });
Query anti-patterns to avoid:
// ❌ Avoid $regex without anchors (can't use index efficiently)
await User.find({ name: { $regex: 'john' } });
// ✅ Better - anchored regex
await User.find({ name: { $regex: '^john', $options: 'i' } });
// ❌ Avoid $ne and $nin (scan entire collection)
await User.find({ status: { $ne: 'deleted' } });
// ✅ Better - use positive matching
await User.find({ status: { $in: ['active', 'pending'] } });
// ❌ Avoid skip() with large offsets
await User.find().skip(10000).limit(10); // Slow!
// ✅ Better - use range-based pagination
await User.find({ _id: { $gt: lastSeenId } }).limit(10);
Answer: A closure is a function that has access to variables from its outer (enclosing) scope, even after the outer function has returned.
Example - Private variables:
function createCounter() {
let count = 0; // Private variable
return {
increment: function() {
count++;
return count;
},
decrement: function() {
count--;
return count;
},
getCount: function() {
return count;
}
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.getCount()); // 2
console.log(counter.count); // undefined (private!)
Example - Function factory:
function createMultiplier(multiplier) {
return function(number) {
return number * multiplier;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
Common pitfall - Loop closures:
// Problem
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // Logs: 3, 3, 3
}
// Solution 1: Use let
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // Logs: 0, 1, 2
}
// Solution 2: Use IIFE
for (var i = 0; i < 3; i++) {
(function(j) {
setTimeout(() => console.log(j), 100);
})(i); // Logs: 0, 1, 2
}
Answer:
== (Loose equality): Compares values after type coercion=== (Strict equality): Compares values AND types without coercionType Coercion Rules:
// String to Number
'5' == 5 // true (string converted to number)
'5' === 5 // false (different types)
// Boolean to Number
true == 1 // true (true becomes 1)
false == 0 // true (false becomes 0)
'1' == true // true ('1' -> 1, true -> 1)
// null and undefined
null == undefined // true (special case)
null === undefined // false
// Object to primitive
[1] == 1 // true ([1].valueOf() -> '1' -> 1)
[1,2] == '1,2' // true (array converted to string)
// Falsy values
0 == false // true
'' == false // true
null == false // false (null only equals undefined)
Best Practice: Always use === unless you specifically need type coercion.
Answer:
this refers to the context in which a function is executed.
| Context | this refers to |
|---|---|
| Global | window (browser) / global (Node) |
| Object method | The object |
| Regular function | undefined (strict) / window (non-strict) |
| Arrow function | Lexical this (inherited from outer scope) |
| Constructor | New instance |
| Event handler | The element that received the event |
Examples:
// Object method
const obj = {
name: 'Object',
greet() {
console.log(this.name); // 'Object'
}
};
// Arrow function lexical this
const obj2 = {
name: 'Object2',
greet: () => {
console.log(this.name); // undefined (lexical scope)
},
delayedGreet() {
setTimeout(() => {
console.log(this.name); // 'Object2' (arrow inherits this)
}, 100);
}
};
// Explicit binding
function greet() {
console.log(`Hello, ${this.name}`);
}
const person = { name: 'Aditya' };
greet.call(person); // Hello, Aditya
greet.apply(person); // Hello, Aditya
const boundGreet = greet.bind(person);
boundGreet(); // Hello, Aditya
Answer:
| Feature | var |
let |
const |
|---|---|---|---|
| Scope | Function | Block | Block |
| Hoisting | Yes (initialized as undefined) | Yes (TDZ) | Yes (TDZ) |
| Re-declaration | Allowed | Not allowed | Not allowed |
| Re-assignment | Allowed | Allowed | Not allowed |
Examples:
// Hoisting difference
console.log(a); // undefined (hoisted)
console.log(b); // ReferenceError (TDZ)
var a = 1;
let b = 2;
// Block scope
if (true) {
var x = 1;
let y = 2;
const z = 3;
}
console.log(x); // 1
console.log(y); // ReferenceError
console.log(z); // ReferenceError
// const with objects (reference is constant, not value)
const arr = [1, 2, 3];
arr.push(4); // OK
arr = [1, 2]; // TypeError
const obj = { a: 1 };
obj.b = 2; // OK
obj = { c: 3 }; // TypeError
Answer: JavaScript uses prototypal inheritance - objects inherit from other objects through the prototype chain.
Example:
// Constructor function
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(`${this.name} makes a sound`);
};
function Dog(name, breed) {
Animal.call(this, name); // Call parent constructor
this.breed = breed;
}
// Set up inheritance
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
// Override method
Dog.prototype.speak = function() {
console.log(`${this.name} barks`);
};
const dog = new Dog('Rex', 'German Shepherd');
dog.speak(); // Rex barks
// ES6 class syntax (syntactic sugar over prototypes)
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a sound`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
speak() {
console.log(`${this.name} barks`);
}
}
Answer:
1. Destructuring:
// Array destructuring
const [first, second, ...rest] = [1, 2, 3, 4, 5];
// Object destructuring
const { name, age, city = 'Unknown' } = person;
// Nested destructuring
const { address: { street, zipCode } } = user;
// Function parameters
function greet({ name, age }) {
console.log(`${name} is ${age} years old`);
}
2. Spread operator:
// Array spread
const combined = [...arr1, ...arr2];
const copy = [...original];
// Object spread
const updated = { ...user, name: 'New Name' };
const merged = { ...defaults, ...options };
3. Template literals:
const message = `Hello ${name}, you have ${count} messages`;
// Multi-line strings
const html = `
<div class="card">
<h2>${title}</h2>
<p>${description}</p>
</div>
`;
4. Arrow functions:
const add = (a, b) => a + b;
const square = x => x * x;
const getUser = () => ({ name: 'John', age: 30 });
5. Optional chaining & Nullish coalescing:
// Optional chaining (?.)
const street = user?.address?.street;
const firstItem = arr?.[0];
const result = obj?.method?.();
// Nullish coalescing (??)
const value = input ?? 'default'; // Only null/undefined trigger default
const count = 0 ?? 10; // 0 (different from ||)
Answer:
| Aspect | Functional Programming | Object-Oriented Programming |
|---|---|---|
| Core concept | Pure functions | Objects with state & behavior |
| Data | Immutable | Mutable |
| State | Avoided | Encapsulated in objects |
| Side effects | Minimal | Common |
| Composition | Function composition | Inheritance & composition |
Functional approach:
// Pure functions
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
// Function composition
const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);
const addTax = amount => amount * 1.18;
const applyDiscount = amount => amount * 0.9;
const formatPrice = amount => `₹${amount.toFixed(2)}`;
const calculateFinalPrice = compose(formatPrice, addTax, applyDiscount);
console.log(calculateFinalPrice(1000)); // ₹1062.00
// Higher-order functions
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 }
];
const names = users
.filter(u => u.age >= 25)
.map(u => u.name)
.reduce((acc, name) => acc + ', ' + name);
Object-oriented approach:
class ShoppingCart {
constructor() {
this.items = [];
}
addItem(item) {
this.items.push(item);
}
getTotal() {
return this.items.reduce((sum, item) => sum + item.price, 0);
}
applyDiscount(percent) {
const discount = this.getTotal() * (percent / 100);
return this.getTotal() - discount;
}
}
class PremiumCart extends ShoppingCart {
constructor() {
super();
this.memberDiscount = 10;
}
getTotal() {
return super.getTotal() * (1 - this.memberDiscount / 100);
}
}
Answer:
Callbacks (Traditional):
// Callback hell
getUser(userId, (user) => {
getOrders(user.id, (orders) => {
getOrderDetails(orders[0].id, (details) => {
console.log(details);
// More nesting...
});
});
});
Promises (ES6):
// Chain promises
getUser(userId)
.then(user => getOrders(user.id))
.then(orders => getOrderDetails(orders[0].id))
.then(details => console.log(details))
.catch(error => console.error(error));
// Promise.all - parallel execution
const [users, products] = await Promise.all([
fetchUsers(),
fetchProducts()
]);
// Promise.race - first to complete
const result = await Promise.race([
fetch('/api/data'),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), 5000)
)
]);
// Promise.allSettled - all complete regardless of success/failure
const results = await Promise.allSettled([
fetchData1(),
fetchData2(),
fetchData3()
]);
Async/Await (ES7):
// Clean, synchronous-looking code
async function getUserOrders(userId) {
try {
const user = await getUser(userId);
const orders = await getOrders(user.id);
const details = await getOrderDetails(orders[0].id);
return details;
} catch (error) {
console.error('Error fetching data:', error);
throw error;
}
}
// Parallel execution with async/await
async function fetchDashboardData() {
const [users, orders, products] = await Promise.all([
fetchUsers(),
fetchOrders(),
fetchProducts()
]);
return { users, orders, products };
}
Answer:
Pattern 1: Try-catch with async/await:
async function fetchData() {
try {
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
if (error.name === 'AbortError') {
console.log('Request was cancelled');
} else if (error instanceof TypeError) {
console.error('Network error:', error);
} else {
console.error('Fetch error:', error);
}
throw error;
}
}
Pattern 2: Error-first result tuple:
// Utility function
async function to(promise) {
try {
const result = await promise;
return [null, result];
} catch (error) {
return [error, null];
}
}
// Usage
const [error, user] = await to(fetchUser(id));
if (error) {
console.error('Failed to fetch user:', error);
return;
}
console.log('User:', user);
Pattern 3: Higher-order error handler:
// Express.js async handler
const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
app.get('/users/:id', asyncHandler(async (req, res) => {
const user = await User.findById(req.params.id);
if (!user) throw new AppError('User not found', 404);
res.json(user);
}));
Answer:
Common workflow:
# Create feature branch
git checkout -b feature/user-authentication
# Make changes and stage
git add -A
git status
# Commit with meaningful message
git commit -m "feat: add JWT authentication middleware"
# Push to remote
git push -u origin feature/user-authentication
# Keep branch updated with main
git fetch origin
git rebase origin/main
# Squash commits before merge
git rebase -i HEAD~3
# Create pull request (via GitHub/GitLab)
# After review, merge and clean up
git checkout main
git pull origin main
git branch -d feature/user-authentication
Useful commands:
# View commit history
git log --oneline -n 10
git log --graph --all --oneline
# Undo changes
git checkout -- <file> # Discard working changes
git reset HEAD <file> # Unstage file
git reset --soft HEAD~1 # Undo last commit, keep changes
git reset --hard HEAD~1 # Undo last commit, discard changes
# Stash changes
git stash
git stash list
git stash pop
git stash apply stash@{1}
# Cherry-pick specific commit
git cherry-pick <commit-hash>
# Resolve merge conflicts
git mergetool
git add <resolved-file>
git merge --continue
Answer:
# Navigation and file operations
cd /path/to/directory
ls -la # List all files with details
pwd # Print working directory
mkdir -p dir1/dir2 # Create nested directories
cp -r source dest # Copy recursively
mv old new # Move/rename
rm -rf directory # Remove directory forcefully
# File viewing and editing
cat file.txt # View entire file
less file.txt # Paginated view
head -n 20 file.txt # First 20 lines
tail -f logfile.log # Follow log file in real-time
nano file.txt # Edit with nano
vim file.txt # Edit with vim
# Searching
grep -r "pattern" . # Search recursively
grep -rn "error" --include="*.js" # Search in JS files with line numbers
find . -name "*.log" # Find files by name
find . -type f -mtime -1 # Files modified in last day
# Process management
ps aux | grep node # Find node processes
top # System monitor
htop # Better system monitor
kill -9 <PID> # Force kill process
lsof -i :3000 # Find what's using port 3000
# Network
curl -X GET http://localhost:3000/api
wget http://example.com/file
netstat -tulpn # List listening ports
ss -tulpn # Modern alternative
# Package management (Ubuntu)
sudo apt update
sudo apt install nodejs
sudo apt upgrade
# Permissions
chmod 755 script.sh # Make executable
chown user:group file # Change ownership
# SSH
ssh user@server
scp file.txt user@server:/path
ssh-keygen -t rsa -b 4096
Answer:
Browser Rendering Pipeline:
Performance Optimization Techniques:
<!-- Critical CSS inline -->
<style>
/* Above-the-fold CSS */
</style>
<!-- Non-critical CSS async -->
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<!-- Preload critical resources -->
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
<!-- Defer non-critical JavaScript -->
<script defer src="app.js"></script>
<script async src="analytics.js"></script>
JavaScript optimizations:
// Debounce scroll/resize handlers
const debounce = (fn, wait) => {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(() => fn.apply(this, args), wait);
};
};
window.addEventListener('resize', debounce(handleResize, 250));
// Use requestAnimationFrame for animations
function animate() {
// Update animation
element.style.transform = `translateX(${position}px)`;
requestAnimationFrame(animate);
}
// Lazy load images
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
});
document.querySelectorAll('img[data-src]').forEach(img => observer.observe(img));
// Avoid layout thrashing
// Bad
elements.forEach(el => {
const height = el.offsetHeight; // Force layout
el.style.height = height + 10 + 'px'; // Trigger reflow
});
// Good - batch reads and writes
const heights = elements.map(el => el.offsetHeight);
elements.forEach((el, i) => {
el.style.height = heights[i] + 10 + 'px';
});
Answer: Elasticsearch is a distributed, RESTful search and analytics engine built on Apache Lucene. It’s used for:
Basic example with Node.js:
const { Client } = require('@elastic/elasticsearch');
const client = new Client({ node: 'http://localhost:9200' });
// Create index
await client.indices.create({
index: 'products',
body: {
mappings: {
properties: {
name: { type: 'text' },
description: { type: 'text' },
price: { type: 'float' },
category: { type: 'keyword' }
}
}
}
});
// Index document
await client.index({
index: 'products',
body: {
name: 'iPhone 15',
description: 'Latest Apple smartphone with advanced features',
price: 999.99,
category: 'electronics'
}
});
// Search
const result = await client.search({
index: 'products',
body: {
query: {
multi_match: {
query: 'apple phone',
fields: ['name', 'description']
}
}
}
});
Answer:
Key Ceremonies I’ve participated in:
Example sprint story:
### User Story: User Authentication
As a user, I want to log in securely so that I can access my personalized dashboard.
**Acceptance Criteria:**
- [ ] Users can log in with email/password
- [ ] JWT tokens are issued on successful login
- [ ] Invalid credentials show error message
- [ ] Session expires after 24 hours
**Story Points:** 5
**Sprint:** 14
These questions may come up based on your resume projects. Be ready to discuss them in depth.
Answer: “I built an intelligent chat application using the MERN stack (MongoDB, Express.js, React.js, Node.js) integrated with Google Gemini API.
Key Technical Decisions:
io.on(‘connection’, (socket) => { socket.on(‘message’, async (data) => { // Store message in MongoDB const message = await Message.create({ content: data.content, conversationId: data.conversationId, role: ‘user’ });
// Get AI response
const aiResponse = await geminiService.generateResponse(
data.content,
data.conversationHistory
);
// Emit AI response back
socket.emit('ai-response', {
content: aiResponse,
role: 'assistant'
}); }); }); ```
class GeminiService { constructor() { this.genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY); this.model = this.genAI.getGenerativeModel({ model: ‘gemini-pro’ }); }
async generateResponse(prompt, history = []) { const chat = this.model.startChat({ history: history.map(msg => ({ role: msg.role, parts: [{ text: msg.content }] })) });
const result = await chat.sendMessage(prompt);
return result.response.text(); } } ```
const conversationSchema = new Schema({
userId: { type: Schema.Types.ObjectId, ref: 'User' },
title: String,
messages: [{
role: { type: String, enum: ['user', 'assistant'] },
content: String,
timestamp: { type: Date, default: Date.now }
}],
createdAt: { type: Date, default: Date.now }
});
Challenges solved:
Live on GitHub: github.com/adityaSrivastava29/AI-Powered-Chatbot”
Answer: “This platform allows users to create and interact with customizable AI personas with distinct personalities, built with MERN stack and OpenAI GPT integration.
Architecture Overview:
┌─────────────────────────────────────────────────────────────┐
│ React.js Frontend │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │Persona Config│ │Chat Interface│ │ User Management │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Express.js API Layer │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Auth Routes │ │Persona Routes│ │ Chat Routes │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
┌──────────────────────┼──────────────────────┐
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ MongoDB │ │ OpenAI API │ │ Redis │
│(User/Persona)│ │ GPT │ │ (Cache) │
└─────────────┘ └─────────────┘ └─────────────┘
Key Features implemented:
const personaSchema = new Schema({
userId: { type: Schema.Types.ObjectId, ref: 'User' },
name: String,
personality: {
traits: [String], // ['friendly', 'professional', 'humorous']
tone: String, // 'casual' | 'formal' | 'enthusiastic'
expertise: [String] // ['technology', 'cooking', 'travel']
},
systemPrompt: String, // Custom instructions for the AI
avatarUrl: String,
isPublic: { type: Boolean, default: false }
});
const buildSystemPrompt = (persona) => {
return `You are ${persona.name}.
Your personality traits: ${persona.personality.traits.join(', ')}.
Your tone is ${persona.personality.tone}.
Areas of expertise: ${persona.personality.expertise.join(', ')}.
${persona.systemPrompt}
Stay in character throughout the conversation.`;
};
for (let i = messages.length - 1; i >= 0; i–) { const tokens = estimateTokens(messages[i].content); if (tokenCount + tokens > maxTokens) break; tokenCount += tokens; optimized.unshift(messages[i]); }
return optimized; };
// Request batching for multiple quick messages const batchedResponses = await processBatchedRequests(pendingMessages);
// Caching similar queries
const cachedResponse = await redis.get(prompt:${hashPrompt(prompt)});
**Live Demo:** adityasri.in/ai-persona-chat-frontend"
---
### Q32: How did you handle authentication and security in your projects?
**Answer:**
"I implemented secure JWT-based authentication with proper security measures:
```javascript
// JWT Token generation
const generateTokens = (userId) => {
const accessToken = jwt.sign(
{ userId },
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);
const refreshToken = jwt.sign(
{ userId },
process.env.JWT_REFRESH_SECRET,
{ expiresIn: '7d' }
);
return { accessToken, refreshToken };
};
// Auth middleware
const authenticate = async (req, res, next) => {
try {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
throw new AppError('No token provided', 401);
}
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = await User.findById(decoded.userId).select('-password');
if (!req.user) {
throw new AppError('User not found', 401);
}
next();
} catch (error) {
if (error.name === 'TokenExpiredError') {
return res.status(401).json({ message: 'Token expired' });
}
next(error);
}
};
// Password hashing with bcrypt
const hashPassword = async (password) => {
const salt = await bcrypt.genSalt(12);
return bcrypt.hash(password, salt);
};
// Input validation with express-validator
const validateUser = [
body('email').isEmail().normalizeEmail(),
body('password').isLength({ min: 8 })
.matches(/^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)/),
handleValidationErrors
];
Security measures implemented:
Answer: “Working with external APIs like OpenAI and Gemini, robust error handling is critical:
class AIService {
async generateResponse(prompt, options = {}) {
const maxRetries = 3;
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await this.callAPI(prompt, options);
return response;
} catch (error) {
lastError = error;
// Handle specific error types
if (error.status === 429) {
// Rate limit - exponential backoff
const delay = Math.pow(2, attempt) * 1000;
console.log(`Rate limited. Retrying in ${delay}ms...`);
await this.sleep(delay);
continue;
}
if (error.status === 503) {
// Service unavailable - retry with backoff
await this.sleep(attempt * 2000);
continue;
}
if (error.status === 400) {
// Bad request - don't retry, fix the input
throw new AppError('Invalid request to AI service', 400);
}
if (error.status >= 500) {
// Server error - retry
await this.sleep(1000);
continue;
}
throw error;
}
}
// All retries exhausted
console.error('AI service failed after retries:', lastError);
throw new AppError('AI service temporarily unavailable', 503);
}
// Fallback response
getDefaultResponse() {
return "I'm having trouble processing that request. Please try again.";
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
Key patterns:
Answer: “I’m a Full-Stack Software Engineer with over 3 years of experience building scalable web applications. I graduated with a B.Tech in Computer Science from Noida Institute of Engineering and Technology with 8.4 CGPA in 2022.
Currently, I work at Cognizant Technology Solutions where I’ve architected and delivered production-ready applications using React.js, TypeScript, Node.js, and PostgreSQL. My work serves 100,000+ active users with 99.9% uptime.
Key Achievements:
I’m also AWS Certified Developer - Associate and have hands-on experience with Docker, Kubernetes, and CI/CD pipelines using GitHub Actions.
On the side, I’ve built personal projects like an AI Chat Application using MERN stack with Google Gemini API and a Custom AI Persona Platform with OpenAI GPT integration.
I’m excited about Mirable Solutions because your focus on product development using the MERN stack aligns perfectly with my experience and passion for building cutting-edge solutions.”
Answer: “At Cognizant, we had a critical performance issue where our API response times were significantly impacting the user experience for an application serving 100,000+ users.
The Problem:
My Approach:
Analyzed query patterns - Identified the most frequently accessed endpoints and their database queries
Implemented Redis caching strategy:
```javascript
const getUser = async (userId) => {
// Check cache first
const cached = await redis.get(user:${userId});
if (cached) return JSON.parse(cached);
// Query database and cache result
const user = await User.findById(userId);
await redis.setex(user:${userId}, 3600, JSON.stringify(user));
return user;
};
3. **Optimized database queries:**
- Added proper indexes on frequently queried fields
- Used pagination and projection to limit data transfer
- Optimized JOIN queries in PostgreSQL
4. **Implemented query optimization in MongoDB:**
```javascript
// Before: Full document retrieval
const users = await User.find({});
// After: Projection + Indexing
const users = await User.find({}, { name: 1, email: 1 })
.lean()
.limit(100);
Result:
Answer: “At Cognizant, I’ve handled multiple high-pressure situations, and I follow a structured approach:
Example - Production Critical Fix: I was recognized as Star Performer (February 2025) specifically for handling a critical production issue. Here’s what happened:
The same CI/CD automation I built earlier (reducing deployment from 2 hours to 15 minutes) was crucial in meeting this tight deadline.”
Answer: “At Cognizant, I had a situation where our team was debating the testing strategy for a new microservices project. A senior developer preferred minimal unit tests with heavy reliance on manual QA, while I advocated for a comprehensive automated testing approach.
The Situation:
How I handled it:
Outcome:
Key learning: Data and proof-of-concept demonstrations are more persuasive than theoretical arguments.”
Answer: “In the next 3-5 years, I see myself growing along these dimensions:
Technical depth:
Leadership:
Product mindset:
At Mirable Solutions specifically:
Suggested questions to ask:
“What does the typical product development cycle look like here? How do developers collaborate with product and design teams?”
“What’s the current tech stack composition? Are there plans to adopt any new technologies?”
“How is the team structured? Will I be working on a specific product or across multiple projects?”
“What does success look like for a developer in the first 90 days?”
“What are the biggest technical challenges the team is currently facing?”
“How does the team approach code reviews and knowledge sharing?”
“What opportunities are there for learning and professional development?”
| Hook | Purpose |
|——|———|
| useState | Local state |
| useEffect | Side effects |
| useContext | Context consumption |
| useReducer | Complex state logic |
| useMemo | Memoize values |
| useCallback | Memoize functions |
| useRef | Persist values, DOM refs |
| Code | Meaning | |——|———| | 200 | OK | | 201 | Created | | 400 | Bad Request | | 401 | Unauthorized | | 403 | Forbidden | | 404 | Not Found | | 500 | Internal Server Error |
| Operator | Usage |
|———-|——-|
| $eq, $ne, $gt, $lt | Comparison |
| $in, $nin | Array membership |
| $and, $or, $not | Logical |
| $set, $unset, $inc | Update |
| $push, $pull, $addToSet | Array update |
Good luck with your interview tomorrow! 🚀