Client project
Multi-Tenant Inventory SaaS
Inventory, POS & Commerce SaaS
Inventory, POS and an online storefront on one FIFO stock ledger, with every business in its own PostgreSQL database. I built almost all of the backend and most of the frontend.
- Role
- Full-Stack Developer
- Status
- In production
- Period
- 2024 – 2026
- ~250REST endpoints across the admin, storefront and public APIs
- ~80tables in each business’s own database
- 3 languagesEnglish, Bangla and Arabic, right to left
The problem
Small and mid-size retailers often run stock, sales and accounting in separate tools or spreadsheets, so they can’t say with confidence what each sale really earned, how much stock sits in each warehouse, or what it is worth. Businesses funded by investors on profit-sharing terms also need profit distribution they can audit.
Overview
A multi-tenant platform where each business runs its catalogue, warehouses, purchases, point of sale, invoices and reports in one place, in English, Bangla or Arabic. Stock is kept as a ledger of FIFO cost layers, so every sale records its true cost and profit. Optional modules, switched on per business, add an online storefront with a headless CMS, Pathao courier shipping, and an investor profit-sharing engine for Shariah-compliant structures.
How it works
How a request finds its business
Stock as a ledger of cost layers
One sale path for both channels
What I did
- Built almost all of the backend on Node.js, Express and TypeScript, and most of the Next.js frontend, as the main developer in a small team.
- Designed the database-per-tenant model: the API recognises each business from the request’s domain and connects to that business’s own PostgreSQL database, so no query needs a tenant filter.
- Designed the stock ledger: every purchase adds a cost layer, and sales, write-offs and transfers use up the oldest layers first under row locks, so profit is exact and every operation can be reversed.
- Built three separate APIs, for staff, for storefront customers (with its own sign-in) and for public catalogue reads, around one inventory engine.
- Turned online checkout into an ordinary sale, with every line re-priced on the server, so stock, costing, invoices and shipping follow the same path for both channels.
- Generated invoices from templates each business can edit, rendered with Puppeteer at any size from a 58 mm receipt to A4, plus printable barcode labels.
- Integrated Pathao courier booking through an adapter layer, with cached OAuth tokens and signed, deduplicated status webhooks.
- Built the investor engine: profit per investment pool from real FIFO sales data, then shares per agreement with proration, rounding rules, versioned calculations and approvals.
- Moved heavy reporting into PostgreSQL with PL/pgSQL functions and a materialized stock view, so dashboards load in a single round trip.
- Deployed it with multi-stage Docker builds behind Nginx, with a GitHub Actions quality gate on the frontend and a 172-request API collection.
Key decisions
A database per business, picked by domain
Strong isolation, simple per-business backup and restore, and no tenant filter to forget in any query. A business’s admin app and its storefront domains all map to the same database through a cached registry.
Stock as a ledger of cost layers, not a quantity column
Average or current buying prices give the wrong profit as soon as purchase prices change. Tracing every unit sold to the purchase it came from freezes the real profit at sale time and makes cancellations exact.
Online checkout becomes an ordinary sale
One code path means the storefront gets stock checks, FIFO costing, investor attribution and shipping for free, and the two channels can never drift apart.
Highlights
- One inventory behind both in-store POS sales and the online storefront, so stock stays in sync across channels.
- Each business defines its own sale and purchase statuses; a flag on each status decides whether stock moves, with no code change.
- Discounts set on a category apply to everything beneath it in the category tree.
- Auto-generated check-digit barcodes, scanned at the till by camera or scanner.
- Investor figures can be explained later: the policy used is stored with every calculation.
Challenges
- Overselling under concurrency: two cashiers selling the last unit at the same moment. Stock layers are locked in a fixed order inside the transaction, and a database constraint stops any layer going below zero.
- Keeping tenants isolated without slowing the API: connections to each business’s database are created once, shared, health-checked and recycled.
- Reversing operations safely: cancelling a sale or editing a purchase restores the exact layers it touched, and is refused if that stock has already been sold on.
- Unreliable courier APIs: expiring tokens, duplicated or forged webhooks, and different APIs per courier, handled with token caching, signature checks, an inbox table and idempotent booking.
Results
- In production for multiple businesses, each with its own isolated database.
- Adding another courier is a new adapter and its status mappings; nothing else changes.
Want something similar built? Get in touch →