Skip to content
Unity Shoplive capture
The deployed Unity Shop storefront: a top announcement bar over a banner carousel, a row of product category tiles, and a grid of discounted product cards with prices in taka.
Team projectLive on Vercel

Unity Shop

A multi-vendor marketplace, built on Next.js with real-time order updates.

A team-built marketplace covering 22 product categories, twelve currencies, seller onboarding, flash-sale countdowns and a real-time order surface. The published frontend talks to MongoDB directly from Next.js route handlers.

Role
Frontend developer within a team. This page documents the shipped codebase, not an individual contribution split.
Source status
Verified — Read from the package manifests of both public repositories, and from the server-rendered homepage of the deployed site.
Next.jsReactNode.jsExpress.jsMongoDBTailwind CSS

+3 more in the case study

5 min read · 10 sections · 10 technologies

Unity Shop is the largest application I have worked on, and the first one built with other people rather than alone. It is a multi-vendor marketplace: vendors list products, buyers browse and check out, and administrators moderate the catalogue.

The version documented here is the one deployed at the link below. Everything in the next sections was read out of the published repository or out of the deployed page itself.

What the product does

  • 22 product categories

    Electronics, Fashion, Home & Living, Kitchen, Bedroom, Office, Mobiles, Watches, Audio, Cameras, Gaming, Lighting, Beauty, Health, Sports, Outdoor, Books, Stationery, Toys & Baby, Grocery, Tools and Automotive.

  • Twelve-currency storefront

    A currency switcher covering USD, EUR, GBP, JPY, CNY, INR, AED, CAD, AUD, SAR and BDT alongside the Bangladeshi Taka.

  • Seller onboarding

    A "New Seller Products" rail promotes recently listed inventory from vendors who joined the platform.

  • Flash sales with countdowns

    Time-boxed offers with a live countdown and a per-offer discount figure.

  • Local payment methods

    bKash and Nagad are surfaced directly in the checkout messaging, alongside free-delivery thresholds.

  • Real-time channel

    Socket.IO and its client are both in the dependency list, which is what backs the live order surface.

  • Admin analytics

    Recharts is used for the reporting surfaces behind the administrative views.

  • Branch-per-contributor

    The backend repository carries one branch per contributor, which is the clearest record of how the team split the work.

Architecture

  1. 01BrowserServer-rendered Next.js pages
  2. 02Route handlersapp/api/*
  3. 03MongooseModels defined in-process
  4. 04MongoDBPrimary datastore
  5. 05Socket.IOOrder events pushed to clients
The deployed frontend reaches the database through its own route handlers.

A separate Express backend exists on another branch of a teammate's repository; it is described below.

The interesting decision is that Mongoose is a direct dependency of the frontend repository. Rather than running a second Express service for the deployed build, the data access layer lives in Next.js route handlers. For a team that wants one deployment, one environment variable set and one place to look at logs, that is a defensible trade. It also means the database schema is coupled to the app's deploy cycle, and the frontend bundle is responsible for the availability of the data layer.

The server repository

There is a second, separate backend: an Express service in a teammate's repository. Its work lives on a branch named iqram, and the repository carries one branch per contributor (Arif, ahsan, develop, iqram, main, rimiruma, sakib), which is the clearest available evidence of how the team divided the work. The manifest below was read from that branch.

UnityShop-Sever @ iqram — package.json (dependencies)
{
  "name": "backend",
  "dependencies": {
    "@google/genai": "*",
    "bcrypt": "*",
    "bcryptjs": "*",
    "cloudinary": "*",
    "cors": "*",
    "dotenv": "*",
    "express": "*",
    "jsonwebtoken": "*",
    "mongodb": "*",
    "mongoose": "*",
    "multer": "*",
    "node-cron": "*",
    "nodemailer": "*",
    "openai": "*",
    "replicate": "*",
    "sharp": "*",
    "socket.io": "*",
    "stripe": "*",
    "ws": "*"
  },
  "devDependencies": {
    "nodemon": "*"
  }
}
Read directly from the public repository, `iqram` branch.
  • Two authentication libraries. bcrypt and bcryptjs are both present. They implement the same algorithm, so one of them is redundant; a codebase that installs both usually accumulated them at different times.
  • Two real-time transports. socket.io and the raw ws package are both installed. They solve the same problem at different abstraction levels, which is worth knowing before writing any new live feature.
  • sharp for server-side image processing alongside multer for uploads and cloudinary for storage, so media is transformed as well as stored.
  • node-cron for scheduled work, which only makes sense on a long-lived process. This service cannot be deployed to serverless functions without replacing it with a platform cron.
  • nodemailer again on the server side, so transactional mail exists on both sides of this project.

Engineering notes

Media pipeline

multer and cloudinary are both present. Multer handles the multipart request in the route handler and Cloudinary stores the asset, which keeps the Next.js server from holding user uploads on disk. Product imagery on the deployed page is served through the Next.js image optimiser rather than being hot-linked.

package.json (excerpt, dependencies)
{
  "dependencies": {
    "cloudinary": "^2.9.0",
    "jspdf": "^4.2.0",
    "mongoose": "^9.2.1",
    "multer": "^2.0.2",
    "next": "16.1.6",
    "nodemailer": "^8.0.1",
    "react": "19.2.3",
    "recharts": "^3.7.0",
    "socket.io": "^4.8.3",
    "socket.io-client": "^4.8.3",
    "stripe": "^20.3.1"
  }
}
Read directly from the published repository.
package.json (excerpt, scripts)
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint"
  }
}

Document generation

jspdf is in the dependency list, which is how the application produces downloadable order documents from the browser without a server-side PDF service. That keeps invoice generation out of the request path that handles payments.

Transactional email

nodemailer handles outbound mail. On a Vercel deployment there is no long-lived process, so any scheduled or retried mail has to be driven from a request or a platform cron rather than an in-process queue.

Trade-offs

  • One deployable instead of two. Fewer moving parts and a single environment variable set, at the cost of losing a hard network boundary between the UI and the data layer.
  • Client-side PDF generation. No rendering service to operate, but layout fidelity is limited to what the browser engine supports.
  • Currency formatting in the client. A twelve-currency switcher is simple to implement locally and becomes a correctness problem as soon as prices are persisted in more than one currency.

Verification status