Curated interview practice questions with answers. These are practice scenarios, not a verified list of past interview questions.
About Razorpay hiring for MERN Stack roles
Razorpay is an Indian fintech company providing payment gateway, payment links, payroll, business banking through RazorpayX and other tools for businesses. Its engineering and analytics teams work on payment success rates, risk, reconciliation and merchant products. It hires for software engineers, SDETs, data analysts and data scientists.
For MERN Stack roles, this Razorpay practice set focuses most on Express.js and REST APIs (6), React Hooks, State and Performance (5), Modern JavaScript (ES6+) (5), Authentication and Security (4). It has 3 easy, 22 medium, 5 hard questions, so you can start easy and work up to the harder ones.
Company type
Fintech
Headquarters
Bengaluru, India
Main India offices
Bengaluru, Mumbai
Razorpay MERN Stack interview process
Online assessment or assignment
Machine coding or technical round
Design or problem solving round
Hiring manager round
Culture fit round
Typical pattern based on public candidate experiences. Rounds vary by role, team and year.
Solved for RazorpayStart 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 central error-handling middleware with a custom AppError class.
Store a role, or a list of permissions, on the user document and include it in the token or load it in the auth middleware. Write an authorize middleware that checks allowed roles or permissions per route, for example authorize('admin'). Add ownership checks in services, such as an editor editing only their own articles. In React, hide buttons by role for user experience, but always enforce rules on the server.
03
How do you set up npm scripts for development and production in a Node.js API?
Define scripts in package.json and run them with npm run.
"scripts": {
"dev": "node --watch --env-file=.env src/server.js",
"start": "node src/server.js",
"lint": "eslint .",
"test": "vitest run"
}
Modern Node has a built-in --watch flag, so nodemon is optional. npm start and npm test work without the run keyword.
04
Create an AuthContext with a provider and a custom hook.
Analyse the bundle to find the largest modules. Split code by route with React.lazy, lazy load heavy libraries like charts and editors, replace large dependencies with lighter ones, import only needed functions so tree shaking works, and remove unused polyfills. Compress images, serve assets from a CDN with Brotli and long cache headers, and consider SSR or static rendering for landing pages. Track Core Web Vitals after each change.
06
What is the output order of console.log('A'), setTimeout(() => console.log('B'), 0), Promise.resolve().then(() => console.log('C')) and console.log('D')?
The output is A, D, C, B. A and D are synchronous and run immediately on the call stack. The Promise callback C goes into the microtask queue, and the setTimeout callback B goes into the macrotask queue. After the synchronous code finishes, the event loop drains all microtasks first, printing C, and only then runs the timer callback, printing B, even with a 0 ms delay.
07
A developer accidentally pushed the .env file with the MongoDB password and JWT secret to a public repository. What do you do?
Treat the secrets as compromised immediately. Rotate the MongoDB user password, the JWT secret and any API keys; rotating the JWT secret logs everyone out, which is acceptable. Check database and cloud logs for suspicious access. Remove the file, add .env to .gitignore, and purge it from history with a tool like git filter-repo, though rotation matters more. Enable secret scanning and pre-commit hooks to prevent repeats.
BSON is the binary format MongoDB uses to store documents. It extends JSON with types like Date, ObjectId, Decimal128, binary data and 32 or 64-bit integers, and is fast to traverse. ObjectId is the default 12-byte _id value made of a 4-byte timestamp, a 5-byte random value and a 3-byte counter. Because it starts with a timestamp, ObjectIds roughly sort by creation time.
09
Group an array of order objects by their status using reduce.
reduce builds an object whose keys are statuses and values are arrays.
const grouped = orders.reduce((acc, order) => {
(acc[order.status] ||= []).push(order);
return acc;
}, {});
// { paid: [...], pending: [...] }
Newer runtimes also offer Object.groupBy(orders, (o) => o.status), which returns the same shape with a null prototype.
10
Your Node.js API runs on an 8-core server but CPU monitoring shows only one core busy. What do you do?
A Node process runs JavaScript on a single thread, so one instance uses about one core. Run multiple instances: PM2 in cluster mode with -i max, the cluster module, or several containers behind a load balancer such as Nginx. Because instances do not share memory, move sessions, caches and rate-limit counters to Redis, and make sure the app is stateless so any instance can serve any request.
11
How do you protect a public Express API from abuse and brute-force traffic?
Add rate limiting with express-rate-limit, using a Redis store when running multiple instances, and apply stricter limits to login and OTP routes. Set request body size limits, validate all input, require authentication on non-public routes, add helmet headers and timeouts. At the edge, a CDN or web application firewall can block bots and DDoS traffic before it reaches Node. Return 429 with a Retry-After header.
12
How does the JavaScript event loop work with microtasks and macrotasks?
JavaScript runs one call stack. When the stack is empty, the event loop first drains the entire microtask queue, which holds Promise callbacks, queueMicrotask and await continuations. Only then does it pick the next macrotask, such as a setTimeout callback, an I/O callback or a UI event. This is why a resolved Promise callback always runs before a setTimeout of 0 ms scheduled at the same time.
13
When a user logs out or changes password, how do you make sure their existing JWTs stop working?
Stateless JWTs stay valid until expiry, so keep access tokens short-lived and revoke refresh tokens by deleting or flagging them in the database. For immediate invalidation, store a tokenVersion or passwordChangedAt on the user and include it in the token; the auth middleware rejects tokens with an older version. Alternatively keep a Redis denylist of token ids until they expire. Clear the refresh cookie on logout.
14
When should you use useReducer instead of useState?
Use useReducer when state has multiple related fields, when the next state depends on complex logic, or when many event handlers update state in different ways. A reducer is a pure function (state, action) => newState that centralises transitions and is easy to unit test, for example a cart with add, remove, change quantity and clear actions. For simple independent values, useState is clearer.
Each loop iteration goes through phases: timers runs expired setTimeout and setInterval callbacks; pending callbacks runs some deferred system callbacks; poll waits for and runs I/O callbacks; check runs setImmediate callbacks; close callbacks handles events like socket close. Between every callback, Node drains process.nextTick callbacks and then Promise microtasks. Knowing this explains why setImmediate inside an I/O callback always runs before a setTimeout of 0.
16
Show the use of $push, $addToSet, $inc and $pull update operators.
Run the query with .explain('executionStats') and check whether the winning plan is COLLSCAN and how totalDocsExamined compares to nReturned. Create an index matching the filter and sort, following ESR, and confirm it becomes IXSCAN. Also project only needed fields, add limits, use lean in Mongoose, and check the profiler or Atlas Performance Advisor for other slow queries. Re-test with production-like data volume.
18
Tell me about a time you found and fixed a security issue in an application.
Situation: while testing an order history feature, I noticed changing the order id in the URL showed another user's order. Task: fix this insecure direct object reference before release. Action: I added an ownership check in the service layer so queries always filter by the logged-in user's id, wrote tests for cross-user access, and reviewed similar endpoints. Result: we fixed four endpoints and added ownership checks to our review checklist.
19
How would you structure the folders of a production Express API?
A layered structure keeps responsibilities clear: src/routes for route definitions, src/controllers for request and response handling, src/services for business logic, src/models for Mongoose schemas, src/middlewares for auth, validation and errors, src/config for environment and database setup, src/utils for helpers, and tests alongside or in a tests folder. Keep app.js, which builds the app, separate from server.js, which listens, so tests can import the app.
20
Some tests pass locally but fail randomly in CI. How do you handle flaky tests?
Rerun to confirm flakiness, then find the cause instead of adding retries. Common causes are shared database state between tests, tests depending on run order, real timers and dates, network calls to real services, and E2E tests using fixed waits instead of waiting for elements. Isolate data per test, mock time and external APIs, use proper async waits, and quarantine a flaky test with a ticket until fixed.
21
Write a production Dockerfile for a Node.js Express API.
Keep the image small and run as a non-root user.
FROM node:22-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --omit=dev
COPY src ./src
USER node
EXPOSE 5000
CMD ["node", "src/server.js"]
Copying package files first lets Docker cache the dependency layer, so code changes rebuild quickly. Add a .dockerignore with node_modules and .env.
22
What is the difference between a shallow copy and a deep copy?
A shallow copy duplicates only the top level, so nested objects are still shared references. Spread syntax and Object.assign make shallow copies. A deep copy duplicates every nested level. structuredClone(obj) is the built-in way in modern browsers and Node.js and supports Dates, Maps and Sets, while JSON.parse(JSON.stringify(obj)) loses functions, undefined and Dates. In React state updates, shallow copies of each changed level are usually enough.
23
How do you plan a Node.js major version upgrade for a project your team maintains?
Situation: our services were on a Node version nearing end of life. Task: I owned the upgrade to the current LTS. Action: I read the changelog for breaking changes, upgraded locally, ran the test suite, fixed deprecated APIs and incompatible native packages, and rolled out to staging, then one production service, watching metrics. Result: all services moved within two sprints with no incidents, and I added an LTS calendar to our planning.
24
A setInterval inside useEffect always logs the initial count value instead of the updated one. Why, and how do you fix it?
This is a stale closure: the effect ran once with an empty dependency array, so its callback captured count from the first render forever. Fixes include using the functional updater setCount((c) => c + 1) so you do not need to read count, adding count to the dependencies with proper cleanup, or storing the latest value in a ref that the interval reads.
25
How does the useEffect dependency array work and why is cleanup important?
useEffect runs after render. With no array it runs after every render, with an empty array only after mount, and with dependencies whenever any of them change by Object.is comparison. The function you return is the cleanup, which runs before the next effect and on unmount. Use it to remove listeners, clear intervals, abort requests and close sockets, otherwise you get leaks and duplicate subscriptions.
26
Write an Express router with CRUD routes for a products resource.
REST models data as resources identified by URLs, uses standard HTTP methods, is stateless, and returns representations like JSON with proper status codes. Use nouns, for example GET /orders and POST /orders. GET, PUT, DELETE, HEAD and OPTIONS are idempotent: repeating them gives the same server state. POST is not idempotent, and PATCH usually is not, which matters when clients retry requests.
28
What strategies can you use to version a REST API?
The most common approach is URL versioning such as /api/v1/orders, which is simple and cache friendly. Alternatives are a custom header or a media type like Accept: application/vnd.app.v2+json, which keeps URLs clean but is harder to test in a browser. Whatever you choose, make additive changes without a new version, announce deprecations, and run old and new versions side by side during migration.
29
Write a docker-compose file to run a MERN app locally.
Three services on one network:
services:
mongo:
image: mongo:8
volumes:
- mongo-data:/data/db
api:
build: ./server
environment:
MONGO_URI: mongodb://mongo:27017/shop
JWT_SECRET: dev-secret
ports:
- "5000:5000"
depends_on:
- mongo
web:
build: ./client
ports:
- "8080:80"
depends_on:
- api
volumes:
mongo-data:
The API reaches the database through the service name mongo.
30
Write a deep clone function that handles nested objects, arrays and dates.
In production prefer structuredClone, but interviewers often ask for a manual version.
function deepClone(value, seen = new WeakMap()) {
if (value === null || typeof value !== 'object') return value;
if (value instanceof Date) return new Date(value);
if (seen.has(value)) return seen.get(value);
const copy = Array.isArray(value) ? [] : {};
seen.set(value, copy);
for (const key of Object.keys(value)) copy[key] = deepClone(value[key], seen);
return copy;
}
The WeakMap prevents infinite loops on circular references.
Tips to crack the Razorpay MERN Stack interview
Learn how a payment gateway works end to end, including authorisation, settlement, refunds and chargebacks.
Practise API testing with tools like Postman and understand webhooks if you are applying for QA roles.
Read Razorpay's engineering blog to understand its scale challenges and use them in design discussions.
Keep a one-minute story ready for every project on your CV: problem, your part, tools, result.
Practise the hard questions on this page aloud; Razorpay interviewers usually ask follow-ups on your first answer.
Frequently asked questions
How many rounds are there in the Razorpay MERN Stack interview?
Candidates usually go through 5 stages: Online assessment or assignment; Machine coding or technical round; Design or problem solving round; Hiring manager round; Culture fit round. The exact rounds change with the role, team, location and your experience, so confirm the process with the recruiter.
Which topics should I prepare for the Razorpay MERN Stack interview?
This practice set leans on Express.js and REST APIs (6), React Hooks, State and Performance (5), Modern JavaScript (ES6+) (5), Authentication and Security (4). Revise those first, then practise explaining one or two of your own projects end to end, because most Razorpay interviewers ask you to walk through real work.
Are these the exact questions asked at Razorpay?
No. These are curated practice questions that match the skills and difficulty usually tested in fintech interviews like Razorpay's. They are not a leaked or verified list of past questions.
Where does Razorpay hire MERN Stack professionals in India?
Razorpay has large teams in Bengaluru, Mumbai. Openings and locations change often, so check the Razorpay careers page and filter by city and experience.