A production-grade B2B SaaS platform for small medical shops in India. Built as a monorepo with a NestJS backend and Next.js 14 frontend.
| Layer | Technology |
|---|---|
| Frontend | Next.js 14 (App Router), React 18 |
| Backend | NestJS 10, TypeORM, Passport-JWT |
| Database | PostgreSQL 16 |
| Cache/Queue | Redis 7, BullMQ |
| Language | TypeScript 5 (strict mode) |
| Monorepo | Turborepo + pnpm workspaces |
- Node.js ≥ 20
- pnpm ≥ 9 —
npm i -g pnpm - Docker + Docker Compose — for local PostgreSQL & Redis
cd medshop
pnpm installdocker compose up -dThis starts:
| Service | Port | Credentials |
|---|---|---|
| PostgreSQL | 5433 |
medshop / medshop123 |
| Redis | 6379 |
— |
The API ships with sensible defaults in apps/api/.env. Review and update if needed:
NODE_ENV=development
PORT=3001
DB_HOST=localhost
DB_PORT=5433
DB_USERNAME=medshop
DB_PASSWORD=medshop123
DB_DATABASE=medshop_dev
REDIS_HOST=localhost
REDIS_PORT=6379
JWT_SECRET=super-secret-jwt-key-change-in-production-min-32-chars
JWT_ACCESS_EXPIRY=15m
JWT_REFRESH_EXPIRY=7d
APP_URL=http://localhost:3000
API_URL=http://localhost:3001
⚠️ ChangeJWT_SECRETbefore deploying to production.
pnpm --filter @medshop/types buildcd apps/api
pnpm migration:runTerminal 1 — Backend API (port 3001):
cd apps/api
pnpm devTerminal 2 — Frontend (port 3000):
cd apps/web
pnpm dev- API health: http://localhost:3001/api/health
- Frontend: http://localhost:3000
medshop/
├── apps/
│ ├── api/ # NestJS backend
│ │ └── src/
│ │ ├── auth/ # Register, login, JWT refresh
│ │ ├── shops/ # Shop profile management
│ │ ├── users/ # Staff management (RBAC)
│ │ ├── products/ # Product catalog + batch inventory
│ │ ├── sales/ # POS / billing + stock deduction
│ │ ├── purchases/ # Inward stock from suppliers
│ │ ├── health/ # Health check endpoint
│ │ └── common/ # Guards, decorators, filters, DTOs
│ │
│ └── web/ # Next.js 14 frontend
│ └── src/
│ ├── app/
│ │ ├── login/ # Login page
│ │ ├── register/ # Shop registration
│ │ └── (dashboard)/
│ │ ├── dashboard/ # Stats & alerts
│ │ ├── products/ # Product catalog
│ │ ├── sales/ # Sales list + New Sale (POS)
│ │ ├── purchases/ # Purchase recording
│ │ ├── users/ # Staff management
│ │ └── settings/ # Shop profile
│ ├── lib/ # Axios API client
│ └── providers/ # Auth, React Query providers
│
├── packages/
│ └── types/ # Shared TS enums, interfaces, utils
│
├── docker-compose.yml # Local PostgreSQL & Redis
├── turbo.json # Turborepo pipeline config
├── tsconfig.base.json # Shared TS compiler options
└── pnpm-workspace.yaml # Workspace definitions
- Multi-tenancy — Row-level
shop_idisolation enforced at guard, service, and query layers - RBAC — Three roles:
OWNER,MANAGER,STAFFwith granular permission checks - JWT Auth — Access token (15 min) + refresh token (7 days, httpOnly cookie)
- GST Compliance — Per-product GST %, HSN codes, tax breakdowns on every sale
- Money in Paise — All monetary values stored as integers to avoid floating-point errors
- Atomic Stock — Sale creation atomically deducts batch quantities in a DB transaction
- Batch Tracking — FEFO (First Expiry First Out), per-batch pricing, expiry alerts
- Soft Deletes — Products, users, and shops use
@DeleteDateColumn()for safe deletion
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /api/auth/register |
No | Register shop + owner |
| POST | /api/auth/login |
No | Login, get tokens |
| POST | /api/auth/refresh |
No | Refresh access token |
| GET | /api/health |
No | Health check |
| GET | /api/shops/me |
Yes | Get current shop profile |
| PATCH | /api/shops/me |
Yes | Update shop (OWNER only) |
| GET | /api/users |
Yes | List staff |
| POST | /api/users |
Yes | Add staff (OWNER only) |
| GET | /api/products |
Yes | List products (searchable) |
| POST | /api/products |
Yes | Add product |
| GET | /api/products/low-stock |
Yes | Low stock alerts |
| GET | /api/products/expiring-soon |
Yes | Expiring batches |
| GET | /api/sales |
Yes | List sales |
| POST | /api/sales |
Yes | Create sale (POS) |
| GET | /api/sales/:id/bill |
Yes | Download sale bill PDF |
| GET | /api/sales/dashboard |
Yes | Dashboard stats |
| GET | /api/purchases |
Yes | List purchases |
| POST | /api/purchases |
Yes | Record purchase |
| GET | /api/purchases/:id/bill |
Yes | Download purchase bill PDF |
# Install all dependencies
pnpm install
# Start infra
docker compose up -d
# Stop infra
docker compose down
# Build everything
pnpm build
# Type-check all packages
pnpm typecheck
# Build only shared types
pnpm --filter @medshop/types build
# Run pending API migrations
pnpm --filter @medshop/api migration:run
# Show API migration status
pnpm --filter @medshop/api migration:show
# Seed demo data (API)
pnpm --filter @medshop/api seed
# Run API in dev mode
pnpm --filter @medshop/api dev
# Run frontend in dev mode
pnpm --filter @medshop/web dev| Variable | Default | Description |
|---|---|---|
NODE_ENV |
development |
Runtime environment |
PORT |
3001 |
API server port |
DB_HOST |
localhost |
PostgreSQL host |
DB_PORT |
5433 |
PostgreSQL port |
DB_USERNAME |
medshop |
PostgreSQL user |
DB_PASSWORD |
medshop123 |
PostgreSQL password |
DB_DATABASE |
medshop_dev |
PostgreSQL database name |
DB_SSL |
false |
Enable Postgres SSL (set true for Neon) |
REDIS_HOST |
localhost |
Redis host |
REDIS_PORT |
6379 |
Redis port |
JWT_SECRET |
(change in production) | JWT signing key (≥32 chars) |
JWT_ACCESS_EXPIRY |
15m |
Access token TTL |
JWT_REFRESH_EXPIRY |
7d |
Refresh token TTL |
APP_URL |
http://localhost:3000 |
Frontend URL (for CORS) |
API_URL |
http://localhost:3001 |
Backend URL |