Skip to content

Architecture overview

How the storefront, admin dashboard, mobile app and API fit together, and what each one talks to.

BabyMart has three apps that people use and one API behind all of them. The storefront, the admin dashboard and the mobile app never touch the database. They send HTTP requests to the API, and only the API reads and writes MongoDB. A few outside services handle sign-in, payments, images and email.

flowchart LR
  subgraph Clients
    W["Storefront<br/>Next.js · :3000"]
    A["Admin dashboard<br/>Vite + React · :5173"]
    M["Mobile app<br/>React Native"]
  end
  API["API<br/>Express · :8000"]
  DB[("MongoDB")]
  W -- "REST + Bearer JWT" --> API
  A -- "REST + Bearer JWT" --> API
  M -- "REST + Bearer JWT" --> API
  API --> DB
  FB["Firebase Auth"] -. "ID token" .-> W
  FB -. "ID token" .-> A
  W -- "Checkout Session" --> ST["Stripe"]
  ST -- "webhook" --> W
  ST -- "webhook" --> API
  API --> SSL["SSLCommerz"]
  API --> IMG["Cloudinary / S3"]
  API --> SMTP["SMTP email"]

Read it left to right. Each client signs the user in, keeps a JWT, and sends it as Authorization: Bearer <token> on every protected call. The API checks the token, applies role rules, and talks to MongoDB and the outside services.

Storefront

The customer shop in apps/web, built with Next.js 16 and React 19. It has 52 pages and 3 route handlers of its own, for Stripe and OAuth. See Storefront.

Admin dashboard

The staff console in apps/admin, built with Vite and React 19. It covers the catalog, orders, users, vendors, content and purchases. See Admin dashboard.

Mobile app

The React Native app in apps/mobile, with 28 screens. It installs on its own, outside the pnpm workspace. See Mobile app.

API

The Express 5 and Mongoose 9 server in apps/api, with 297 endpoints. It owns all data, pricing and permissions. See API server.

The three clients use different HTTP tools, but each follows the same contract: call <base URL>/api/... with a Bearer token.

Client HTTP helper Base URL Where the token lives
Storefront fetchWithConfig (exported as fetchData) API_ENDPOINT on the server, NEXT_PUBLIC_API_URL in the browser auth_token cookie (7 days)
Admin dashboard axios instance adminApi VITE_NEXT_PUBLIC_API_URL + /api localStorage key auth-storage
Mobile app fetch wrapper in services/api.ts 10.0.2.2:8000 (Android emulator) or localhost:8000 (iOS) in dev zustand user-storage in AsyncStorage
apps/web/lib/config.ts› fetchWithConfig apps/admin/src/lib/config.ts apps/mobile/src/config/environment.ts› getApiBaseUrl

The storefront also guards routes on the server. apps/web/proxy.ts, which is Next 16’s middleware, reads the auth_token cookie on /user/*, /auth/* and /success. For /user/* and /auth/* it asks the API who the user is with GET /api/auth/profile and redirects as needed. For /success it only checks that the cookie exists. Sign-in and the token are explained in Auth and roles.

The web and admin apps share TypeScript types and UI through three workspace packages. Turborepo builds the packages first, because every build, lint, test and type-check task depends on ^build.

  1. 1@babyshop/typestsc → dist/
  2. 2@babyshop/uisource + tokens.css
  3. 3@babyshop/configshared constants
  4. 4Appsweb · admin · api · docs
What `pnpm build` runs, in dependency order

The mobile app is left out of the pnpm workspace (!apps/mobile in pnpm-workspace.yaml), so Turborepo does not build it. The packages are described in Shared packages.

  • The API is the only source of truth. Order prices come from the Product collection on the server. A total sent by a client is never trusted (apps/api/src/modules/orders/orderPricing.ts).
  • Stateless auth. A signed JWT carries the user id. There are no server sessions, so any API instance can serve any request.
  • Payment status is confirmed by the server. An order counts as paid only after a signed webhook or a check against Stripe or SSLCommerz. See Payments.
  • One design system. Web and admin import the same colour tokens and components from @babyshop/ui.