MERN Interview Notes
Redux Setup
Copy Redux
npm install @reduxjs/toolkit react-redux import { createSlice } from "@reduxjs/toolkit"; const initialState = { user: null, }; const authSlice = createSlice({ name: "auth", initialState, reducers: { login: (state, action) => { state.user = action.payload; }, logout: (state) => { state.user = null; }, }, }); export const { login, logout } = authSlice.actions; export default authSlice.reducer; import { configureStore } from "@reduxjs/toolkit"; import authReducer from "./slices/authSlice"; export const store = configureStore({ reducer: { auth: authReducer, }, }); import React from "react"; import ReactDOM from "react-dom/client"; import { Provider } from "react-redux"; import { store } from "./redux/store"; import App from "./App"; ReactDOM.createRoot(document.getElementById("root")).render(
); import { useDispatch } from "react-redux"; import { login } from "../redux/slices/authSlice"; const dispatch = useDispatch(); const handleLogin = async () => { const response = await loginApi(data); dispatch( login({ id: response.id, username: response.username, email: response.email, accessToken: response.accessToken, }) ); }; import { useSelector } from "react-redux"; const user = useSelector((state) => state.auth.user); console.log(user.username); console.log(user.email); console.log(user.accessToken); import { useDispatch } from "react-redux"; import { logout } from "../redux/slices/authSlice"; const dispatch = useDispatch(); const handleLogout = () => { dispatch(logout()); }; dispatch(login(user)); localStorage.setItem("user", JSON.stringify(user)); redux │── store.js └── slices └── authSlice.js
Route Guards
Copy Route Guards
import { Navigate, Outlet } from "react-router-dom"; import { useSelector } from "react-redux"; const AuthGuard = () => { const user = useSelector((state) => state.auth.user); if (!user?.accessToken) { return
; } return
; }; export default AuthGuard; import { Navigate, Outlet } from "react-router-dom"; import { useSelector } from "react-redux"; const GuestGuard = () => { const user = useSelector((state) => state.auth.user); if (user?.accessToken) { return
; } return
; }; export default GuestGuard; import { BrowserRouter, Routes, Route, } from "react-router-dom"; import AuthGuard from "./guards/AuthGuard"; import GuestGuard from "./guards/GuestGuard"; import Login from "./pages/Login"; import Register from "./pages/Register"; import Dashboard from "./pages/Dashboard"; import Profile from "./pages/Profile"; function App() { return (
}>
} />
} />
}>
} />
} />
); }
Express Rate Limiter
Copy Rate Limiter
npm install express-rate-limit export const apiLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, standardHeaders: true, legacyHeaders: false, message: { success: false, message: "Too many requests. Please try again later.", }, }); export const loginLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 5, standardHeaders: true, legacyHeaders: false, message: { success: false, message: "Too many login attempts. Please try again after 15 minutes.", }, }); export const registerLimiter = rateLimit({ windowMs: 60 * 60 * 1000, // 1 hour max: 5, standardHeaders: true, legacyHeaders: false, message: { success: false, message: "Too many accounts created. Please try again later.", }, }); import express from "express"; import { apiLimiter, loginLimiter, registerLimiter, } from "./middlewares/rateLimiter.js"; const router = express.Router(); router.post("/login", loginLimiter, login); router.post("/register", registerLimiter, register); router.get("/profile", apiLimiter, getProfile); router.put("/profile", apiLimiter, updateProfile); router.get("/products", apiLimiter, getProducts);
Pagination + Search + Filter + Sort
Copy Pagination
export const getTasks = async (req, res) => { try { const userId = req.user.id; const { page = 1, limit = 10, search = "", status, sortBy = "createdAt", order = "desc", } = req.query; const query = { userId, }; // Search if (search) { query.title = { $regex: search, $options: "i", }; } // Filter if (status) { query.status = status; } const skip = (page - 1) * limit; const tasks = await Task.find(query) .sort({ [sortBy]: order === "asc" ? 1 : -1, }) .skip(skip) .limit(Number(limit)); const total = await Task.countDocuments(query); res.json({ tasks, pagination: { total, page: Number(page), totalPages: Math.ceil(total / limit), }, }); } catch (err) { res.status(500).json({ message: err.message, }); } }; const [tasks, setTasks] = useState([]); const [page, setPage] = useState(1); const [search, setSearch] = useState(""); const [status, setStatus] = useState(""); const [sortBy, setSortBy] = useState("createdAt"); const [order, setOrder] = useState("desc"); const limit = 10; const fetchTasks = async () => { const { data } = await axios.get("/api/tasks", { params: { page, limit, search, status, sortBy, order, }, }); setTasks(data.tasks); }; useEffect(() => { fetchTasks(); }, [page, search, status, sortBy, order]); const [searchInput, setSearchInput] = useState(""); const [search, setSearch] = useState(""); useEffect(() => { const timer = setTimeout(() => { setSearch(searchInput); setPage(1); // reset to first page on new search }, 500); return () => clearTimeout(timer); }, [searchInput]);
setSearchInput(e.target.value)} placeholder="Search task..." />
{ setStatus(e.target.value); setPage(1); }} >
All
Pending
Completed
setSortBy(e.target.value)} >
Created Date
Title
setOrder(e.target.value)} >
Ascending
Descending
setPage((p) => p - 1)} > Previous
{page}
setPage((p) => p + 1)} > Next
Dashboard API
Copy Dashboard API
router.get( "/dashboard", authenticateUser, getDashboardData ); import Task from "../models/task.model.js"; export const getDashboardData = async (req, res) => { try { const userId = req.user.id; const [ totalTasks, pendingTasks, inProgressTasks, completedTasks, highPriorityTasks, mediumPriorityTasks, lowPriorityTasks, ] = await Promise.all([ Task.countDocuments({ userId }), Task.countDocuments({ userId, status: "pending", }), Task.countDocuments({ userId, status: "in_progress", }), Task.countDocuments({ userId, status: "completed", }), Task.countDocuments({ userId, priority: "high", }), Task.countDocuments({ userId, priority: "medium", }), Task.countDocuments({ userId, priority: "low", }), ]); return res.status(200).json({ success: true, data: { totalTasks, status: { pending: pendingTasks, inProgress: inProgressTasks, completed: completedTasks, }, priority: { high: highPriorityTasks, medium: mediumPriorityTasks, low: lowPriorityTasks, }, }, }); } catch (error) { return res.status(500).json({ success: false, message: error.message, }); } };