Javascript Interview - Advanced
What is Callback Hell and How to avoid this?
Callback Hell (also known as the “Pyramid of Doom”) refers to a situation in JavaScript (or other asynchronous programming environments) where multiple nested callbacks make code hard to read, understand, and maintain.doSomething(function(result1) { doSomethingElse(result1, function(result2) { doAnotherThing(result2, function(result3) { doFinalThing(result3, function(result4) { // and so on... }); }); }); });This kind of code grows horizontally and becomes difficult to manage because: It’s hard to read.
Error handling becomes messy. Logic is tightly coupled.
How to Avoid Callback Hell -
i) Use Named Functions
Instead of writing anonymous functions inside callbacks, define them separatelyfunction handleResult1(result1) { doSomethingElse(result1, handleResult2); } function handleResult2(result2) { doAnotherThing(result2, handleResult3); } function handleResult3(result3) { doFinalThing(result3, finalCallback); } doSomething(handleResult1);ii) Use Promises
Promises flatten the structure and make the flow easier to follow
doSomething() .then(result1 => doSomethingElse(result1)) .then(result2 => doAnotherThing(result2)) .then(result3 => doFinalThing(result3)) .catch(error => console.error(error));iii) Use Async/Await
Even better readability, and it looks synchronousasync function processAll() { try { const result1 = await doSomething(); const result2 = await doSomethingElse(result1); const result3 = await doAnotherThing(result2); const result4 = await doFinalThing(result3); } catch (error) { console.error(error); } } processAll();What are JavaScript Promises? Explain their states.
A Promise in JavaScript is an object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value.
It’s like a placeholder for a value that will be known in the future — perfect for dealing with asynchronous tasks like API calls, file loading, timers, etc.// Basic Syntax const promise = new Promise((resolve, reject) => { // async operation if (success) { resolve("Data received"); } else { reject("Something went wrong"); } });A Promise has three states
i) PendingInitial state.
The operation is still ongoing (neither fulfilled nor rejected).
Example: waiting for an API response.
ii) Fulfilled
The operation completed successfully.
resolve(value)is called.The promise returns a result.
Handled with
.then()orawait.iii) Rejected
The operation failed.
reject(error)is called.An error is returned.
Handled with
.catch()or atry/catchblock inasync/await.let fetchData = new Promise((resolve, reject) => { let success = true; setTimeout(() => { if (success) { resolve("Data loaded"); } else { reject("Failed to load data"); } }, 2000); }); fetchData .then(data => console.log("Success:", data)) .catch(error => console.error("Error:", error));Once a promise is fulfilled or rejected, it is settled — and can't change its state anymore.
How to handle fetch Data API using Promises?
Thefetch()function is a modern way to make HTTP requests. It returns a Promise that resolves to theResponseobject representing the result of the request.// GET Method using Promise fetch('https://jsonplaceholder.typicode.com/posts/1') .then(response => { if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); // parse JSON from response }) .then(data => { console.log("Fetched data:", data); }) .catch(error => { console.error("Fetch error:", error); });fetch(url): Initiates a request and returns a Promise..then(response => ...): Handles the response object.response.json(): Converts the response body to JSON (also returns a Promise)..then(data => ...): Accesses the actual data..catch(error => ...): Catches any network or parsing errors.
Always check response.ok -This checks if the HTTP status is in the
200–299range (success). Otherwise, you might think it worked even if it returned a 404 or 500.Handling a POST API request using Promises with
fetch()is very straightforward — you just need to: Set themethodto"POST". Includeheaders(especially"Content-Type": "application/json"). Pass the request body as a JSON string usingJSON.stringify().// POST Method using Promise const postData = { title: 'Hello World', body: 'This is a post request example', userId: 1 }; fetch('https://jsonplaceholder.typicode.com/posts', { method: 'POST', headers: { 'Content-Type': 'application/json' // Tells server you're sending JSON }, body: JSON.stringify(postData) // Convert JS object to JSON string }) .then(response => { if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); // Parse JSON response }) .then(data => { console.log("POST successful, response data:", data); }) .catch(error => { console.error("POST failed:", error.message); });How to handle fetch Data API using Async/Await?
Usingasync/awaitwith thefetch()API is a clean and modern way to handle asynchronous data fetching in JavaScript.// GET Method using Async/Await async function fetchData() { try { const response = await fetch('https://jsonplaceholder.typicode.com/posts/1'); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } const data = await response.json(); console.log("Fetched data:", data); } catch (error) { console.error("Fetch error:", error.message); } } fetchData();async function: Declares an asynchronous function.await fetch(...): Waits for thefetch()Promise to resolve.response.ok: Checks if the request was successful.await response.json(): Waits for the response to be parsed to JSON.try...catch: Catches and handles any errors (like network issues or bad responses).
Let's handle a POST API usingasync/await— it’s clean, modern, and easy to read.// POST Method using Async/Await async function postData() { const payload = { title: 'Hello World', body: 'This is a post request using async/await', userId: 1 }; try { const response = await fetch('https://jsonplaceholder.typicode.com/posts', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } const data = await response.json(); console.log("POST successful:", data); } catch (error) { console.error("POST request failed:", error.message); } } postData();async function: Declares an asynchronous function.await fetch(...): Waits for the HTTP request to complete.method: 'POST': Specifies you're sending data.headers: Tells the server the content type is JSON.body: JSON.stringify(...): Converts the JavaScript object to a JSON string.await response.json(): Waits for and parses the JSON response.try...catch: Catches and handles errors like network failures or bad responses.What is the difference between .then() and .catch() in promises?
.then()and.catch()are essential tools for handling Promises in JavaScript — they help you deal with success and error outcomes of asynchronous operations.
i).then()– Handles Success
.then()is used when a Promise is fulfilled (resolved) successfully. It allows you to access the result of the Promise.fetch('https://api.example.com/data') .then(response => response.json()) // handles the fulfilled state .then(data => console.log("Data:", data)); // chained for further processingii)
.catch()– Handles Errors.catch()is used when a Promise is rejected or if an error is thrown anywhere in the chain before it.fetch('https://api.example.com/data') .then(response => response.json()) .then(data => console.log("Data:", data)) .catch(error => console.error("Error:", error)); // handles errorsDifference between Try and Catch in Promises -
i) Purpose
try - Handles success
catch - Handles errors/failures
ii) Receives
try - The resolved value
catch - The error object
iii) Return Type
try - Returns a new Promise
catch - Also returns a new Promise
iv) Usage
try - For continuing flow
catch - For catching and responding to errorsWhat are the differences between Promise.all, Promise.race, Promise.any, and Promise.allSettled?
These four Promise utility methods are powerful for handling multiple asynchronous operations in JavaScript, and each behaves differently based on how the Promises resolve or reject.
i) Promise.all()
It waits for all Promises to resolve. If anyone fails, it rejects immediately.Promise.all([promise1, promise2, promise3]) .then(results => console.log("All resolved:", results)) .catch(error => console.error("One failed:", error));It returns array of all resolved values after all the promises gets resolved.
ii) Promise.race()
It returns the first settled promise (either resolved or rejected).Promise.race([promise1, promise2, promise3]) .then(result => console.log("First settled (resolved):", result)) .catch(error => console.error("First settled (rejected):", error));iii) Promise.any()
It returns the first resolved promise. Ignores rejections unless all fail.Promise.any([promise1, promise2, promise3]) .then(result => console.log("First resolved:", result)) .catch(error => console.error("All failed:", error));iv) Promise.allSettled()
It waits for all Promises to settle, regardless of outcome.Promise.allSettled([promise1, promise2, promise3]) .then(results => { results.forEach(result => { if (result.status === "fulfilled") { console.log("Resolved:", result.value); } else { console.log("Rejected:", result.reason); } }); });Use Case Summary -
i) Use
Promise.all()when all must succeedii) Use
Promise.race()when only the fastest mattersiii) Use
Promise.any()when any one success is enoughiv) Use
Promise.allSettled()when you need all results regardless of success/failureHow do you cancel an ongoing promise?
i) Use AbortController (for fetch requests)const controller = new AbortController(); const signal = controller.signal; fetch('https://jsonplaceholder.typicode.com/posts', { signal }) .then(response => response.json()) .then(data => console.log(data)) .catch(error => { if (error.name === 'AbortError') { console.log('Fetch aborted'); } else { console.error('Fetch error:', error); } }); // Cancel the fetch after 2 seconds setTimeout(() => controller.abort(), 2000);ii) Custom Cancelable Promise Wrapper
You can wrap a Promise to make it cancellable manually, though this only works for operations you control (like timeouts or intervals).function cancellablePromise(executor) { let cancel; const wrapped = new Promise((resolve, reject) => { cancel = () => reject(new Error("Cancelled")); executor(resolve, reject); }); wrapped.cancel = cancel; return wrapped; } const promise = cancellablePromise((resolve, reject) => { setTimeout(() => resolve("Done!"), 3000); }); promise.then(console.log).catch(console.error); // Cancel before it completes setTimeout(() => promise.cancel(), 1000);iii) Reactive Libraries / Promises with Cancel Support
If you're using libraries like,RxJS (with Observables)
Axios (with cancel tokens — now deprecated, but had similar logic)
Bluebird (with cancellable Promises)
How do you handle promises sequentially?
Handling Promises sequentially means making sure each Promise runs only after the previous one has finished — super useful for things like - Making API calls in order, Performing async tasks one-by-one, Respecting rate limits.
i) Usingasync/await(Cleanest Way)async function runSequentially() { const result1 = await fetch('https://jsonplaceholder.typicode.com/posts/1'); const data1 = await result1.json(); console.log("Post 1:", data1); const result2 = await fetch('https://jsonplaceholder.typicode.com/posts/2'); const data2 = await result2.json(); console.log("Post 2:", data2); const result3 = await fetch('https://jsonplaceholder.typicode.com/posts/3'); const data3 = await result3.json(); console.log("Post 3:", data3); } runSequentially();ii) Sequentially Loop Through an Array
const urls = [ 'https://jsonplaceholder.typicode.com/posts/1', 'https://jsonplaceholder.typicode.com/posts/2', 'https://jsonplaceholder.typicode.com/posts/3' ]; async function fetchSequentially(urls) { for (let url of urls) { const res = await fetch(url); const data = await res.json(); console.log(data); } } fetchSequentially(urls);iii) Using
.then()Chainingfetch('https://jsonplaceholder.typicode.com/posts/1') .then(res => res.json()) .then(data1 => { console.log("Post 1:", data1); return fetch('https://jsonplaceholder.typicode.com/posts/2'); }) .then(res => res.json()) .then(data2 => { console.log("Post 2:", data2); return fetch('https://jsonplaceholder.typicode.com/posts/3'); }) .then(res => res.json()) .then(data3 => { console.log("Post 3:", data3); }) .catch(err => console.error("Error:", err));What is async/await, and What are the benefits of using async/await over promises?
async/awaitis syntactic sugar over Promises introduced in ES2017 (ES8). It makes asynchronous code look and behave more like synchronous code — making it easier to read, write, and debug.async function fetchData() { try { const response = await fetch('https://jsonplaceholder.typicode.com/posts/1'); const data = await response.json(); console.log("Data:", data); } catch (error) { console.error("Error:", error); } }asynckeyword: declares a function that returns a Promise.awaitkeyword: pauses execution until the Promise settles (resolves or rejects).
Benefits of Usingasync/awaitOver Promises -
i) Clean, readable code
Feels like synchronous code - easier to understand & maintain
ii) Easier error handling
Usetry/catchinstead of multiple.catch()blocks
iii) Avoids callback hell
Reduces deep.then().then().then()nesting
iv) Great for sequencing
Easy to run async tasks one after another (sequentially)
v) Debug-friendly
Debuggers and stack traces are easier to follow
When Not to Useawait-
i) When you need parallel execution (Promise.all()is better)ii) When handling many short tasks where batching is faster
How do you handle errors with async/await?
Error handling withasync/awaitis one of its biggest strengths — it makes dealing with asynchronous failures clean and readable usingtry...catch.async function fetchData() { try { const response = await fetch('https://jsonplaceholder.typicode.com/invalid-url'); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } const data = await response.json(); console.log("Data:", data); } catch (error) { console.error("Caught error:", error.message); } } fetchData();tryblock - Place yourawaitcalls here.catchblock - Catches: Network errors, JSON parsing errors, Manually thrown errors (likethrow new Error())Always Check
response.okEven if
fetch()succeeds (as in: doesn't throw), it can still return a 404 or 500. So always checkif (!response.ok) { throw new Error("Request failed with status: " + response.status); }What are generators in Javascript, and how do they work?
Generators are special functions in JavaScript that can pause and resume their execution.
They are declared using the
function*syntax and use theyieldkeyword to pause and return a value at each step.function* myGenerator() { yield "First"; yield "Second"; yield "Third"; } const gen = myGenerator(); console.log(gen.next()); // { value: "First", done: false } console.log(gen.next()); // { value: "Second", done: false } console.log(gen.next()); // { value: "Third", done: false } console.log(gen.next()); // { value: undefined, done: true } // yield → Think of it like a return, but with the ability to come back later.i) When you call a generator function, it doesn’t run immediately.
ii) It returns a generator object (an iterator).
iii) Each call to
.next()runs the generator until it hits the nextyield.function* - Declares a generator function
yield - Pauses the function and returns a value
.next() - Resumes the function from where it left off
done - A flag returned by.next()indicating if the generator has finishedGenerators Are Perfect for -
i) Lazy loading / streaming values - Generate values on demand
ii) Custom iterators - Build powerfulfor...ofloops
iii) Async control flows (viayield) - Advanced use with async tools like redux-saga
iv) Pausing expensive computations - Break down long tasks into smaller piecesWhat are mixins in JavaScript?
Mixins are a pattern used to add reusable behaviors (methods/properties) to classes or objects, without using inheritance.Think of them like “plug-in features” you can mix into multiple classes.
JavaScript doesn't support multiple inheritance, but you often want a class to share behaviors from multiple sources.Mixins solve that by letting you compose functionality.
let canEat = { eat() { console.log(`${this.name} is eating`); } }; let canWalk = { walk() { console.log(`${this.name} is walking`); } }; class Person { constructor(name) { this.name = name; } } // Mix behaviors into the class prototype Object.assign(Person.prototype, canEat, canWalk); const user = new Person("Alice"); user.eat(); // Alice is eating user.walk(); // Alice is walkingWhat is currying in JavaScript? Provide an example.
Currying is a technique where a function, instead of taking all arguments at once, takes them one at a time — returning a new function each time until all arguments are received.
In short: Convert a functionf(a, b)intof(a)(b)// Normal function function add(a, b) { return a + b; } // Curried version function curriedAdd(a) { return function(b) { return a + b; }; } console.log(add(2, 3)); // 5 console.log(curriedAdd(2)(3)); // 5Why Use Currying? -
i) Reusability - Create specialized functions by pre-filling args
ii) Functional composition - Easier to chain and combine small functions
iii) Cleaner syntax - Especially in frameworks like React or ReduxExplain the debouncing, and when would you use it?
Debouncing is a technique used to limit how often a function is executed — especially when it's triggered by high-frequency events like Typing, Scrolling, Resizing, Button clicks.
It ensures that the function only runs after a specified delay, and only if the event stops happening during that delay.
Imagine you're typing in a search box — instead of firing a search API request on every keystroke, you debounce it to wait 300ms after you stop typing.function debounce(fn, delay) { let timeout; return function(...args) { clearTimeout(timeout); timeout = setTimeout(() => { fn.apply(this, args); }, delay); }; } // Example usage function searchHandler(query) { console.log("Searching for:", query); } const debouncedSearch = debounce(searchHandler, 500); // Simulate typing debouncedSearch("H"); debouncedSearch("He"); debouncedSearch("Hel"); debouncedSearch("Hell"); debouncedSearch("Hello"); // Only this will trigger after 500msWhen to Use Debouncing -
i) Search inputs - Reduce unnecessary API calls while typing
ii) Window resize events - Avoid performance hits from rapid firing
iii) Scroll-based animations/lazy load - Smooth rendering, fewer updates
iv) Form validation while typing - Don’t validate on every keystrokeExplain the throttling, and when would you use it?
Throttling is a technique that ensures a function is called at most once every X milliseconds, no matter how many times the event is triggered.So even if the event fires constantly, the throttled function only runs at fixed intervals.
Imagine someone trying to call you 100 times in a minute, but your phone only lets one call through every 5 seconds — that’s throttling.function throttle(fn, delay) { let lastCall = 0; return function(...args) { const now = new Date().getTime(); if (now - lastCall >= delay) { lastCall = now; fn.apply(this, args); } }; } // Usage function logScrollPosition() { console.log("Scroll Y:", window.scrollY); } window.addEventListener('scroll', throttle(logScrollPosition, 300)); // In this case, even if the user scrolls rapidly, // the function logs scroll position at most every 300ms.When to Use Throttling -
i) Scroll events - Avoid flooding the browser with too many calls
ii) Window resize - Optimize layout recalculations
iii) Button mash prevention - Prevent spamming API or animations
iv) Continuous input tracking - Log or track at steady intervalsWhat is difference between debouncing and throttling?
i) Definition
Debouncing - Delay execution until after a certain time has passed since the last event.
Throttling - Limits execution to once every specified time interval, no matter how many events happen.
ii) When function runs
Debouncing - After no events occur for the delay period.
Throttling - At regular intervals during continuous event triggering.
iii) Purpose
Debouncing - Ensure the function runs only once after rapid events stop.
Throttling - Ensure the function runs at steady intervals during rapid events.
iv) Common Use Cases
Debouncing - Auto-save, search input filtering, window resizing (after stop typing/resizing).
Throttling - Scrolling events, mouse movements, window resizing (smooth updates while happening).
v) Example Behavior
Debouncing - Wait until user stops typing to send an API request.
Throttling - Track scrolling position every 300ms, even during heavy scroll.
vi) If you want
Debouncing - "Do it once after the user stops doing it"
Throttling - "Do it regularly while the user is doing it"Explain the concept of Event propagation?
Event propagation is the way events travel through the DOM tree.
It happens in three phases,i) Capturing Phase (Trickling Down)
The event starts at the window and travels down to the target element.
ii) Target PhaseThe event reaches the target element (where the event actually occurred).
iii) Bubbling Phase (Bubbling Up)The event then bubbles up from the target to the root (window).
Imagine clicking a button inside a div inside the body:window → document → body → div → button (target) ↑ bubbles back up<div id="outer"> <button id="inner">Click me</button> </div>document.getElementById("outer").addEventListener("click", () => { console.log("Outer clicked"); }, true); // Capturing phase document.getElementById("inner").addEventListener("click", () => { console.log("Inner clicked"); });Controlling Propagation -
i) event.stopPropagation()
Stops further propagation (no bubbling/capturing)
ii) event.stopImmediatePropagation()
Also blocks other listeners on the same element
iii) event.preventDefault()
Prevents default browser behavior (like form submission)How do you prevent the default action of an event?
you can prevent the default action of an event (like a link navigating, a form submitting, etc.) by calling event.preventDefault();<form id="myForm"> <input type="text" name="name" /> <button type="submit">Submit</button> </form> <script> document.getElementById("myForm").addEventListener("submit", function(event) { event.preventDefault(); // Stops the form from submitting console.log("Form submission prevented!"); }); </script>What is the difference between event delegation and event bubbling?
Event bubbling and event delegation are closely related concepts, but they serve different purposes.
i) Event Bubbling
A mechanism where an event starts from the target element and bubbles up through its ancestors.<div id="parent"> <button id="child">Click Me</button> </div>document.getElementById("parent").addEventListener("click", () => { console.log("Parent clicked"); }); document.getElementById("child").addEventListener("click", () => { console.log("Child clicked"); });Bubbling happens automatically unless stopped with
event.stopPropagation().ii) Event Delegation
It is a pattern where you attach one event listener to a parent, and useevent.targetto detect which child element was clicked.
It is useful for handling events on dynamically added elements or reducing multiple listeners.document.getElementById("parent").addEventListener("click", (event) => { if (event.target.tagName === "BUTTON") { console.log("Button clicked:", event.target.textContent); } });How does JavaScript handle memory leaks?
JavaScript has automatic memory management — meaning it uses a Garbage Collector to free up memory that is no longer needed.
If something is no longer reachable (nothing references it anymore), the garbage collector removes it from memory. But Memory leaks can still happen when you accidentally keep references to things you no longer need!
Chrome DevTools, Firefox DevTools, etc. can detect and profile memory leaks.
Tools like Heap Snapshots help you track down objects still stuck in memory.
How to Minimize/Prevent Memory Leaks -
i) Uselet,constinstead of globals - Limits variable scope
ii) Clean up timers and listeners -clearInterval(),removeEventListener()when no longer needed
iii) Nullify references manually -element = null;when done
iv) Use WeakMap/WeakSet for caching - They don't prevent garbage collectionHow do you use Web Workers in JavaScript for multi-threading?
Web Workers allow JavaScript to run in the background on a separate thread.This means they can do heavy computations without blocking the main UI thread — keeping your app smooth and responsive!
JavaScript is single-threaded by default, but Web Workers give you a way to simulate multi-threading.
How to Use Web Workers -
i) Create a separate JS file for the worker code.ii) Instantiate a
Workerin your main script.iii) Use
postMessage()to send data to the worker.iv) The worker responds back with
postMessage()too.// worker.js // Listen for messages from main thread self.addEventListener('message', function(event) { const result = event.data * 2; // Simple computation self.postMessage(result); // Send result back });// main.js // Create a new worker const worker = new Worker('worker.js'); // Listen for messages from the worker worker.addEventListener('message', function(event) { console.log("Result from worker:", event.data); }); // Send data to the worker worker.postMessage(10);Why Use Web Workers? -
i) Heavy calculations (e.g., image processing, video encoding).ii) Real-time data crunching (e.g., financial charts, game physics).
iii) Background syncing, offline tasks.
What is memoization in Javascript, and how is it used?
Memoization is a caching technique, It stores the results of expensive function calls and returns the cached result when the same inputs occur again.This improves performance, especially for heavy or repetitive computations.
// Simple Example Without Memoization function slowFunction(num) { console.log("Calculating..."); return num * num; } console.log(slowFunction(5)); // "Calculating..." then 25 console.log(slowFunction(5)); // "Calculating..." again! // The function recomputes even if the input is the same.// Memoized Version function memoize(fn) { const cache = {}; return function(...args) { const key = JSON.stringify(args); // create a unique key based on arguments if (cache[key]) { return cache[key]; } else { const result = fn.apply(this, args); cache[key] = result; return result; } }; } // Usage const optimizedFunction = memoize(slowFunction); console.log(optimizedFunction(5)); // "Calculating..." then 25 console.log(optimizedFunction(5)); // Instant 25 from cache (no "Calculating...") // Now the result is cached, and the second call is instant!What are pure functions in JavaScript?
A pure function is a function that:Given the same input, always returns the same output.
Does not cause any side effects (like modifying a global variable, DOM manipulation, writing to a file, etc.).
function add(a, b) { return a + b; } console.log(add(2, 3)); // Always 5 console.log(add(2, 3)); // Always 5, no side effectsWhy Pure Functions are Important -
Easier to debug and test.
Predictable behavior (no surprises!).
Great for functional programming (React, Redux, etc.).
Makes code more reusable and maintainable.
What are some best practices for securing JavaScript applications?
i) Avoid Exposing Sensitive Information -Never store API keys, passwords, or secrets in your frontend code.
Use environment variables on the server side and APIs to handle sensitive tasks.
ii) Use HTTPS -Always serve your website over HTTPS.
Protects against man-in-the-middle attacks and data sniffing.
iii) Input Validation and Sanitization -Validate and sanitize all user input (both on frontend and backend).
Prevents attacks like Cross-Site Scripting (XSS) and SQL Injection.
iv) Prevent Cross-Site Scripting (XSS)Never directly inject user input into the DOM.
Escape or sanitize outputs.
Use security libraries like: DOMPurify (to sanitize HTML safely)
v) Use Content Security Policy (CSP)Set up a CSP header to restrict what resources the browser can load.
Helps prevent XSS attacks by blocking unauthorized scripts.
<meta http-equiv="Content-Security-Policy" content="default-src 'self'">vi) Be Careful with
eval()andnew Function()Avoid using
eval()— it can execute arbitrary code, which is very dangerous.Same caution applies to
setTimeout(string)andsetInterval(string).
vii) Keep Dependencies UpdatedVulnerabilities often come through outdated libraries.
Regularly update your npm/yarn packages.
Use tools like:
npm audit, Snyk, Dependabot
viii) Use Proper Authentication and AuthorizationUse libraries like OAuth, JWT for authentication.
Implement role-based access control (RBAC) on the server.
Never trust client-side authentication alone!
ix) Protect Against Cross-Site Request Forgery (CSRF)
For APIs, use anti-CSRF tokens.
Set appropriate CORS (Cross-Origin Resource Sharing) policies.
x) Limit Error Messages
Avoid showing stack traces or internal errors to users.
Log detailed errors server-side, but show generic errors to users.