All Case Studies
Mechanic Bazaar at a glance
The business, the users and the stack behind the rebuild.
| Item | Detail |
|---|---|
| Business | B2B marketplace for automotive and heavy equipment parts, Kashmiri Gate, New Delhi |
| Users | Wholesalers and manufacturers, resellers, Mechanic Bazaar staff |
| Rebuild | Repository commits from Dec 2025 to Jan 2026 |
| Stack | Laravel 12, Inertia, React, TypeScript, Radix UI |
| Hosting | Hostinger, LiteSpeed web server, GitHub Actions releases |
Challenge
Mechanic Bazaar is a New Delhi B2B marketplace where wholesalers list automotive and heavy equipment parts and resellers buy them to sell on Amazon, eBay and other channels. Dcrayon rebuilt the platform.
A parts market with three kinds of user
Mechanic Bazaar trades from Kashmiri Gate, the old Delhi wholesale district for vehicle parts, and its catalogue runs to thousands of part numbers: bearing kits for Case backhoe loaders, urea filters, fan belts and pulleys, hydraulic seal kits, radiator parts, wiper motors, tractor parts and vehicle filters. The business sits between two groups. Wholesalers and manufacturers hold the stock. Resellers, many of them small online sellers, buy at wholesale prices and sell the same parts on Amazon, Flipkart, eBay and Walmart, where a heavy equipment part can sell for more than its Delhi wholesale price. Mechanic Bazaar's own team approves accounts, keeps the catalogue in order and runs orders between the two. Our brief was to rebuild the platform so that each of these three groups had its own working area, and so that a reseller could see what a part was worth on other marketplaces before buying it.
Where the first version of the platform fell short
New marketplace features were slow to add to the first build
The earlier version was a Node.js (Express) server with a React front end and a Drizzle database layer. As the business grew, every marketplace feature took longer to add, and there was no clean way to bring third-party resellers onto the catalogue alongside the wholesalers who supply it. For a business whose model depends on resellers buying in volume, that gap limited how far the marketplace could grow on the old code.
Wholesalers had to be vetted before they could sell
A B2B parts market only works if buyers can trust the sellers. Mechanic Bazaar needed every wholesaler to register a company and pass a document check, with GST certificates, identity proof and business registration reviewed by staff, before their stock appeared to resellers. A check run outside the platform, by email and spreadsheet, leaves no record of who approved whom and when.
Resellers needed the wholesale price and the marketplace price side by side
A reseller deciding whether to buy a Case 580 bearing kit needs three numbers together: the wholesale price, a realistic price for listing the same part on eBay or Amazon, and the fee that marketplace takes. If those sit on different websites, every purchase means leaving the platform to do the arithmetic, which slows buying for the users the marketplace is built around.
Thousands of part numbers had to be loaded and kept current
Heavy equipment parts are identified by part number, compatible models and condition, and both stock and prices change often. A catalogue of this size cannot stay accurate if every change has to be typed into a web form one product at a time.
Solution
Choosing Laravel and Inertia for a three-role platform
We kept React for the interface, which the first build already used and which suits a dashboard-heavy product, and moved the server side to Laravel 12, a PHP framework, joined to React through Inertia. Inertia lets Laravel controllers return React pages directly, so routing, permissions and data loading stay on the server in one codebase. Staying on the Express server would have meant adding a separate API for each new area and enforcing every permission rule twice, once in the API and once in the interface. With Inertia, each rule lives once, on the server, which matters on a marketplace with three roles and many such rules. The interface is written in TypeScript, with components built on Radix UI. This is the kind of build our custom web application development team carries out.
Wholesaler onboarding with company KYC and approval
A wholesaler registers as a user and then creates a company record in the companies table: company name, legal type (proprietor, LLP, private limited or public limited), GST or VAT number, business address, industry and whether it trades online, offline or both. KYC (know your customer, the identity and business checks a regulated seller expects) documents are uploaded to the kyc_documents table, typed as a GST certificate, ID proof, business registration, bank statement or address proof. Each document carries a status of pending, approved or rejected, a rejection reason, the time it was reviewed and the staff member who reviewed it, so every approval has an owner and a date.
The company's own kyc_status moves from not submitted to under review, approved or rejected. Until a wholesaler is approved, the CheckApproval middleware sends them to a pending-approval page instead of their dashboard, and the same middleware logs out any account that staff have deactivated. The admin area shows a count of verifications waiting, so the queue is visible without anyone having to ask.
A parts catalogue keyed on SKU, part number and fit
Products live in the products table, each linked to the wholesaler who supplies it, a category and a brand. Every part carries its SKU, name, part number, brand, model, year, condition (new by default), weight, dimensions, minimum and maximum order quantities, stock level, unit, images and videos, plus a JSON field for specifications such as fitment. Indexes on slug, SKU, wholesaler and category keep lookups fast as the catalogue grows.
When a wholesaler adds a product, the server validates it before anything is saved: the name is required and limited to 255 characters, the category and any extra categories must exist, the SKU must be unique, the rupee price and stock must be zero or more, the minimum order quantity must be at least one, and the GST rate must sit between 0 and 100 with an optional HSN code (the tax classification code used for GST). A ProductObserver then cleans the product's web address: it builds a slug from the name if none was given, strips characters that are not letters, numbers or hyphens, and adds a suffix if the slug is already taken, so no two parts share a page address. The same observer sets the US dollar price from the rupee price at the platform's fixed conversion rate whenever the rupee price changes, which keeps the two currencies in step without anyone editing both.
Buyers find parts through a mega menu of the main groups, a search box with suggestions, category and subcategory pages, and product pages that show both currencies. A cascading category picker lets wholesalers place a part under a category and subcategory in one step.
Marketplace pricing that shows resellers their margin
The marketplace_pricing table holds, for each product, a row per marketplace (Amazon, eBay or Walmart) with that marketplace's price in rupees and dollars, its platform fee percentage, a shipping estimate in both currencies and a link to the live listing. The eBay row is a reference price for listing the part there, set above the wholesale price, so a reseller looking at a seal kit sees the wholesale price, the reference listing price and the platform's fee on one page and can work out the margin before buying. The home page also has a "high margin products" section for parts the team has flagged as high-profit. On 13 October 2026 every product in the home page sections carried an eBay listing link. Resellers who want help running the selling side can use our eBay account management service.
Reseller plans, wallets, orders and prebooking
Resellers choose from six plans stored in subscription_plans, each with a product limit, monthly and yearly prices, the marketplaces it covers and flags for whether packaging, shipping and order management are included. The entry plan covers Amazon alone; the middle plans add Flipkart, Meesho and JioMart; the Pro and Enterprise plans cover Amazon, eBay and Walmart, with the Enterprise plan adding API access and bulk upload.
Each reseller has a wallet in user_wallets, with a balance and currency, and every movement is written to wallet_transactions as a credit or debit with the balance after the change and a reference to what caused it. Because each movement records the balance after it, every change to a wallet can be traced. Orders in the orders table link a seller to a wholesaler and move through six statuses, from pending and confirmed to processing, shipped, delivered or cancelled, with subtotal, tax, shipping cost and total held separately. Order lines sit in order_items, quantity-based prices in price_tiers, and a prebooking route lets a reseller reserve a part.
Bulk catalogue updates through Excel and a product API
Staff can import and export the catalogue in Excel or CSV. The ImportRequest validation accepts only xlsx, xls or csv files up to 10 MB and asks the user to choose what happens to duplicates: skip them, update the existing product, or stop the import. The ImportExportService and its ProductsImport and ProductsExport classes do the work, and the import modal in the interface shows the choice before the file is sent.
For larger jobs, a key-protected product API lets a desktop tool and an Excel-based manager fetch products by SKU or page through the catalogue, and create or update products, including their marketplace pricing, in bulk. A sync command fills in eBay marketplace pricing for products that have an eBay link but no pricing row yet, with a dry-run option that shows what it would add first.
How the application is layered
Requests come in through Laravel routes grouped by role. The CheckRole middleware allows only the roles named for each group and returns a 403 (access refused) to anyone else, and CheckApproval sits behind it for wholesaler and seller areas. Controllers are split by area: Admin, Wholesaler, Seller, a public storefront and an API namespace. Controllers validate the request, call Eloquent models (Laravel's database classes) and services such as ImportExportService, and return an Inertia page. The HandleInertiaRequests middleware shares the data every page needs, such as the cart count, the mega menu categories, unread orders and waiting verifications, so each React page receives it without its own request. Roles are defined once, in a UserRole enum with three values: admin (Administrator), wholesaler (Wholesaler/Manufacturer) and seller (End Reseller).
Naming rules in the data model
The data model follows one set of conventions. Tables are plural and snakecase (`marketplacepricing, wallettransactions`, `kycdocuments), every primary key is a UUID rather than a counting number, so IDs cannot be guessed, and foreign keys are named after the table they point to (wholesalerid`, `sellerid, companyid`). Money is always a fixed-point decimal with two places, never a floating-point number, and wherever two currencies exist the columns come in pairs ending `inr and _usd`. Status fields are enums with a fixed list of values, so an order cannot be saved as "shiped" by mistake, and the columns used for filtering (status, seller, wholesaler, category) are indexed.
A React component set organised from small to large
The interface is assembled from three layers of React components. The base layer is a set of interface primitives built on Radix UI: buttons, inputs, selects, dialogs, tabs, tables, forms, toasts, tooltips, a rich-text editor and image and video upload fields. The middle layer holds the marketplace's own components, such as product-card, product-quick-view-modal, mega-menu, header-search, search-combobox, currency-selector, language-selector, CascadingCategorySelect, data-table, import-modal, high-profit-products, cookie-consent-banner and lazy-image. The top layer is the page shells: a DashboardLayout with an app sidebar for the three working areas, and outer header and footer components for the public site. Pages are grouped by who uses them, under Admin, Wholesaler, Seller, Public and Auth.
Releases built the same way every time
Releases run through a GitHub Actions workflow. It installs the PHP dependencies without development packages, installs the JavaScript packages from the lock file, builds the front-end assets, deploys the package to the Hostinger server and then runs the post-deployment commands over SSH. Because the build happens in the workflow rather than on someone's laptop, every release is produced from the repository in the same steps.
Access control and data protection
Access is controlled on the server, not just hidden in the interface. Each route group names the role it serves, wholesalers and sellers must also pass the approval check, and deactivated accounts are logged out on their next request. Every write goes through server-side validation, including the file-type and size rules on imports. Forms carry Laravel's CSRF token (a per-session value that stops other websites submitting forms as a logged-in user), and the session cookie is marked secure, HTTP-only and same-site. The site redirects plain HTTP to HTTPS with a 301 and sends a content security policy that upgrades any insecure request. The product API requires a key. The data model also stores consent records, cookie preferences, newsletter subscriptions with an audit log, and requests from people asking about their personal data.
Search groundwork and hosting
Each page sets its own title, description and canonical address through Inertia's head component, with Open Graph and Twitter card tags for sharing. The site publishes structured data, the machine-readable descriptions search engines read, for the organisation, a site search box and an auto parts store. Our technical SEO team covers this kind of groundwork. The site is hosted on Hostinger behind a LiteSpeed web server, the front end is split into separate vendor bundles for React, Inertia, Radix and shared utilities, so the browser can cache the libraries apart from the application code, product images are served in sized variants, and a lazy-image component holds off loading images until they scroll into view.
Mechanic Bazaar data model: core tables
The real table names behind the features described above.
| Entity | Table | What it holds |
|---|---|---|
| Wholesaler company | companies | Legal type, GST or VAT number, trading type, KYC status |
| KYC document | kyc_documents | Document type, status, rejection reason, reviewer and time |
| Part | products | SKU, part number, model, condition, stock, rupee and dollar prices |
| Marketplace price | marketplace_pricing | Amazon, eBay or Walmart reference price, fee percentage, shipping estimate |
| Wallet movement | wallet_transactions | Credit or debit, amount, balance after, reference |
Mechanic Bazaar React component set
How the interface is built up from small parts to page shells.
| Component level | Examples from this build | Used on |
|---|---|---|
| Interface primitives | button, input, select, dialog, tabs, table, form, toast | Every page |
| Marketplace components | product-card, mega-menu, search-combobox, currency-selector | Public catalogue and product pages |
| Working-area components | data-table, import-modal, CascadingCategorySelect | Admin and wholesaler areas |
| Page shells | DashboardLayout with app-sidebar, outer header and footer | Dashboards and the public site |
Mechanic Bazaar access and data controls
The controls that decide who can do what, and how each was confirmed.
| Control | Where it applies | How it was checked |
|---|---|---|
| Role check returning 403 | Admin, wholesaler and seller route groups | CheckRole middleware in the repository |
| Approval gate and deactivation logout | Wholesaler and seller areas | CheckApproval middleware in the repository |
| Server-side validation | Product create and edit, imports | Controller rules and ImportRequest |
| CSRF token and secure, HTTP-only session cookie | Every form and session | Live response headers |
| HTTPS redirect and insecure-request upgrade | Whole site | 301 from HTTP and policy header, live |
Results
What wholesalers, resellers and staff can do now
The rebuilt platform is live at mechanicbazaar.com. A wholesaler can register a company, upload KYC documents, see their approval status and, once approved, list and manage parts with validated prices, stock and tax codes. A reseller can browse thousands of parts with rupee and dollar prices, compare the wholesale price with marketplace reference prices and fees on the same page, choose a plan that matches the channels they sell on, place and track orders through six clear statuses, prebook parts, and see every wallet movement. Mechanic Bazaar's team reviews verifications, orders, content and the catalogue from one admin area, and can update thousands of products at once through the import tools and the product API.
Sales and traffic figures for the marketplace are not part of this case study.
Key takeaways
Lessons from building a three-sided parts marketplace
A B2B marketplace is three products in one: a supplier portal, a buyer's catalogue and an operations back office. Building them as separate areas in one Laravel and React codebase, with roles enforced in middleware rather than in the interface, kept each group's rules in one place. Putting marketplace pricing and fees next to the wholesale price turned the catalogue into a buying tool for resellers rather than a price list.
The project also shows how much a parts catalogue depends on its back-office tools. When products are identified by part numbers and change often, the import rules, the duplicate-handling choice and the product API matter as much as the storefront. Our article on features a Laravel ecommerce website needs covers more of these choices, and our ecommerce solutions page explains how we scope marketplace builds.
If you run a wholesale or reseller marketplace, tell us what your users need to do.









