MediOne
Online pharmacy and delivery for Djibouti, with a NestJS API and an Expo app that tracks orders on a map
MediOne lets customers in Djibouti order medicine from the nearest branch and follow the courier to their door. I am building the backend that serves the customer, courier and admin clients, and the customer app on iOS and Android.
The problem
The client runs several pharmacy branches, each with its own stock in Odoo. Customers had no way to see what a branch carries, order it, or know when a delivery would arrive, and couriers had no shared view of the orders they were carrying.
What I built
A NestJS modular monolith where every module is split into domain, application, infrastructure and presentation layers. Customers sign in with a phone number and one-time code, browse a branch-aware catalogue, check out with a map-picked address, and watch the order move on a live map. Background work runs in a separate BullMQ worker built from the same Docker image.
Results
- Foundation phase shipped: authentication, catalogue, cart, orders with status history, courier simulation and health checks.
- Every pull request runs the test suite against real PostgreSQL 16 and Redis service containers in GitHub Actions.
- Customer app talks to the API through types generated from the backend's OpenAPI document, so contract drift fails the build.
- Market
- Djibouti
- Clients served
- Customer, courier and admin apps
Features
- Phone number and one-time code sign-in with refresh tokens, login history and lockout
- Catalogue and stock per branch
- Cart and checkout with an address picked on a map inside the delivery zone
- Delivery fee and free-delivery threshold in Djibouti francs
- Order status history with a live tracking map
- Idempotent order creation so a retried request never places two orders
- Push notifications through Expo
- Offline-friendly query cache that survives app restarts
How it's put together
One API and one worker process from a single image, backed by PostgreSQL, Redis and MinIO. The mobile app keeps the map renderer behind an adapter so it can be swapped.
- Environment validated with Zod at boot, so a missing secret stops the process before it serves traffic
- Correlation IDs, rate limiting, caching and structured pino logs in the HTTP layer
- MikroORM v7 migrations, pinned to Node 24 after tracing module-identity failures under Jest on older Node versions
- MapLibre native module behind a map adapter interface, built with EAS development clients
- Docker Compose stack for PostgreSQL, Redis and MinIO, with the API and worker behind an opt-in profile
Related projects
Mobile apps2026Dube
Offline-first debt book for shopkeepers: an Android app, a sync API and an admin panel
Solo buildExpoReact Nativeexpo-sqlite+15
Web apps2026Church management platform
Multi-tenant church management: members, households, attendance, forms, analytics and WhatsApp messaging
Solo buildNext.jsReactTanStack Query+11- Odoo & ERP2025
Odoo implementation and training
Soprosys clients in Djibouti
Treasury forecasting, CRM rollout and staff training for companies running Odoo
Led the buildOdoo 16Python