MOD — Engineering Deep Dive
A closer look at the engineering decisions behind MOD, including architecture, backend systems, authentication, API design and the technical trade-offs made throughout development.

System Architecture
React
Express
PostgreSQL
The project follows a traditional full-stack architecture, separating concerns between the client application, server and database.
Rather than relying on an external commerce platform, every major feature—from authentication to ordering—was designed as part of a custom backend.
The goal wasn't simply to build another online store. It was to understand how every layer of a modern commerce application works together.
Technology Stack
Frontend
React
UI Framework
Built the application around reusable components and predictable rendering.
Tailwind CSS
Styling
Created a consistent design system with utility-first styling.
The frontend was built with React and focuses on reusable UI, responsive layouts and modular state management.
Some of the key areas include:
Authentication flows Product browsing Wishlist management Shopping cart Checkout Responsive navigation Fuzzy search Custom filtering
Backend
Express
REST API
Implemented authentication, products, orders and wishlist endpoints.
PostgreSQL
Database
Stored users, products, orders and relationships using a relational model.
JWT
Authentication
Secured protected routes and user-specific resources.
The backend is built with Node.js and Express.
Responsibilities include:
- User authentication
- Product management
- Wishlist persistence
- Order processing
- Search endpoints
- Validation
- Error handling
- JWT authorization
Authentication Flow
Login Form
REST API
JWT
Protected Route
Authentication is handled using JWT tokens.
The flow consists of:
Registration > Login > Token generation > Protected routes > User-specific resources || Features such as wishlists and ordering are only available after successful authentication.
Engineering
API Architecture
Rather than exposing database models directly, the application is organised around REST resources.
Examples include:
Authentication
Authentication, authorization and secure account management.
JWT
🔒 Protected
REST
/auth/registerSecure account creation, authentication and session management. The authentication layer protects customer-specific resources while keeping public catalogue endpoints openly accessible.
/auth/registerCreate a new customer account.
/auth/loginAuthenticate user credentials and issue a signed JWT.
/users/meReturn the authenticated user's profile.
Product Catalogue
The catalogue powers discovery across fashion and skincare products. It supports advanced filtering, category navigation, slug-based routing, featured collections and product recommendations.
Public
REST
Search
Filtering
/products/search/suggestionsFuzzy search across product catalog.
/products/featuredGet feautured products
/products/bestsellersGet Bestseller products
/products/category/{categorySlug}/subcategory/{subcategorySlug}Get products by category and subcategory
/products/gender/{genderSlug}Get products by gender
/products/slug/{slug}Get a single product by slug
/products/{id}/relatedGet related products
/products/{id}/availabilityCheck product availability
/products/{id}Retrieve a single product.
/products/{id}Update a product (Admin Only)
/products/{id}Delete a product (Admin Only)
/productsGet all products with filters
/productsCreate a new product (Admin Only)
/products/{id}/permanentPermanently delete a product - hard delete (Admin Only)
Orders
/api/ordersCreate an order.
/api/ordersRetrieve user orders.
Engineering Slice
Fuzzy Product Search
Problem
Finding products should feel forgiving.
People rarely remember exact product names, spellings or even the correct category. Instead of requiring precise matches, the search experience was designed to interpret intent and surface relevant products even when the query is incomplete or slightly incorrect.
Approach
Rather than relying on SQL string matching, a dedicated fuzzy-search layer indexes multiple product attributes and assigns each a different weight.
This keeps the search logic reusable while producing results that feel much closer to how people naturally search online.
Implementation Journey
Search Component
const [query, setQuery] = useState("");
useEffect(() => {
const timer = setTimeout(() => {
fetchProducts(query);
}, 300);
return () => clearTimeout(timer);
}, [query]);The input is debounced before contacting the API. This dramatically reduces unnecessary requests while keeping the interface responsive.
products.routes.ts
router.get(
"/search",
productController.searchProducts
);Keeping search behind its own endpoint allows the frontend to evolve independently from filtering, recommendations and category browsing.
products.controller.ts
export async function searchProducts(req, res) {
const { q } = req.query;
const results =
await productService.search(q);
res.json(results);
}The controller only coordinates HTTP concerns. Business logic stays inside the service layer.
search.service.ts
const fuse = new Fuse(products, {
keys: [
"name",
"brand",
"category",
"tags",
],
threshold: 0.35,
});
return fuse.search(query);Each searchable field contributes differently to the final score. Product names have more influence than tags, producing more relevant matches while remaining tolerant of spelling mistakes.
Example Response
[
{
"name": "Premium Black Denim Jacket",
"score": 0.02
},
{
"name": "Black Bomber Jacket",
"score": 0.08
}
]Only the most relevant products are returned to the client, keeping payloads small and rendering fast.
Outcome
✓ Forgiving search experience
✓ Better product discovery
✓ Modular search service
✓ Easy to extend with autocomplete, synonyms and AI ranking
CLIENT
Sign In
Capturing user intent
Authentication begins at the interface.
The Sign In and Sign Up pages are intentionally lightweight. Their only responsibility is to collect user input, validate obvious mistakes on the client and hand the credentials to the authentication layer.
Keeping these components free from business logic means they remain easy to reason about and easy to reuse. They don't know how users are authenticated—they simply express the user's intent.
const handleSubmit = async (e) => {
e.preventDefault();
await login({
email: formData.email,
password: formData.password,
});
navigate(from, { replace: true });
}APPLICATION
Authentication Context
Separating interface from authentication
Instead of allowing pages to communicate directly with the API, every authentication request passes through the AuthContext.
This creates a single source of truth for the authenticated user. Once a login succeeds, every component—from the navigation bar to protected routes—reacts automatically without each page having to manage its own authentication state.
This separation also makes replacing the underlying authentication provider significantly easier in the future.
const login = async (credentials) => {
const data = await authService.login(credentials)
setUser(data.user)
setIsAuthenticated(true)
return data
}CLIENT API
API Communication
One gateway to the backend
Every request passes through a shared API layer.
Besides keeping HTTP requests consistent across the application, this abstraction automatically stores the returned JWT, restores existing sessions after refreshes and ensures future authenticated requests always include the required authorization token.
By centralising these responsibilities, the rest of the application never needs to know where authentication data is stored.
const data = await api.post(
"/auth/login",
credentials
)
localStorage.setItem(
"token",
data.token
)SERVER
Authentication Pipeline
A predictable request pipeline
On the server every request follows the same architecture.
The router defines the available endpoint.
Validation middleware ensures incoming data satisfies the required schema.
The controller coordinates the request without containing business logic.
Finally, the service performs the authentication workflow by querying the database, verifying passwords and generating a JWT.
Keeping these responsibilities separate dramatically simplifies maintenance as the project grows.
router.post(
"/login",
loginValidator,
validate,
authController.login
)DOMAIN
Identity Verification
Verifying identity securely
Passwords are never stored in plain text.
During registration each password is hashed using bcrypt before being persisted.
When logging in, the submitted password is compared against the stored hash rather than the original password.
Only after a successful comparison is a signed JSON Web Token created containing the user's identity and permissions.
This allows future requests to be authenticated without repeatedly querying the database.
const match = await bcrypt.compare(
password,
user.password_hash
)
const token = jwt.sign(
payload,
process.env.JWT_SECRET,
{
expiresIn: "7d",
}
)CLIENT
Protected Experience
Security as part of navigation
Rather than every page checking authentication independently, protected areas of the application are wrapped inside a dedicated ProtectedRoute component.
Unauthenticated users are redirected to the sign-in page while preserving their original destination. Once authentication succeeds they continue exactly where they intended to go.
This creates a smoother experience while ensuring sensitive functionality always remains behind authenticated routes.
if (
requireAuth &&
!isAuthenticated
) {
return (
<Navigate
to="/signin"
state={{
from: location,
}}
replace
/>
)
}Challenges
Some of the biggest engineering challenges included:
- ->structuring a scalable folder architecture
- ->keeping frontend and backend responsibilities separate
- ->designing reusable API endpoints
- ->balancing aesthetics with performance
- ->maintaining consistency across responsive layouts
Each challenge influenced how later features were implemented.
Looking Back
If rebuilding MOD today, there are several areas I would improve.
These include:
- ->introducing server-side rendering
- ->stronger typing across the backend
- ->automated testing
- ->image optimisation
- ->AI-assisted product recommendations
Despite these improvements, the project successfully achieved its original objective: designing and building a complete commerce platform from scratch while exploring the intersection of fashion, skincare and personalised shopping.
Back to Part I
Return to the product story, design philosophy and user experience behind MOD.
Continue to Part II