Amana for Ghost Burgers · 2025, actively developed
Amana
Three-sided delivery logistics platform: a REST API, web dashboard, and native rider app in one monorepo.
Built + maintained by Iano
Visit live site ↗Summary
Amana is a three-sided delivery logistics platform connecting restaurants, riders, and customers, delivered as a single monorepo with a Node.js/Express API, a Next.js admin dashboard, and a React Native (Expo) rider app. It runs real-time order and rider tracking over WebSockets, takes payments through M-Pesa, and handles routing via Google Maps, all deployable as a containerized stack.
Context
A delivery operation needed one connected system covering order intake, dispatch, live rider tracking, payments, and admin oversight, instead of stitching together separate restaurant, rider, and back-office tools.
Role & team
Iano, a two-person marketing agency I co-founded. Both founders engineered the build.
Stack
- Express
- Socket.IO
- Mongoose / MongoDB 7
- Redis 7
- Next.js 16
- React 19
- Tailwind CSS 4
- Zustand
- React Native 0.83
- Expo 55
- Expo Router
- M-Pesa (Safaricom Daraja)
- Google Maps Platform
- Cloudinary
- Turborepo + pnpm workspaces
- Docker
- PM2
- Nginx
- GitHub Actions
Key decisions
- Chose a Turborepo monorepo over separate repos so the API, dashboard, rider app, and shared validation/config evolve together with shared schemas (`packages/shared`).
- Chose Socket.IO over polling to power real-time order status and rider location updates.
- Chose M-Pesa (Daraja) STK-push as the payment rail, matching the Kenyan market.
- Chose a native React Native rider app over a mobile web view for reliable background location, notifications, and on-the-road performance.
Architecture
Three apps sit under `apps/`: an Express REST + WebSocket API (`config`, `middleware`, `models`, `routes`, `services`, `socket`, scheduled `jobs`), a Next.js admin dashboard, and an Expo rider app (Zustand stores, location/notification/socket services), alongside shared `packages/`. MongoDB is the primary store with Redis for caching; real-time events flow through Socket.IO; payments via M-Pesa STK-push with a callback endpoint; geocoding/directions via Google Maps; media via Cloudinary; JWT access/refresh auth with OTP verification. Deploy: API on PM2 + Nginx, dashboard via Docker, mobile via EAS build/submit.
Challenges
- Coordinating three runtimes in one codebase: Solved with Turborepo + pnpm workspaces and shared validation/config packages.
- Real-time, role-aware data flow: Orders, riders, and admin each see the right live view, scoped by role through the API and Socket.IO channels.