• 4 min read

Turning Messenger food orders into a real delivery system

Table of Contents

MiaGo is a campus and municipality-based food delivery platform that turns informal Messenger-based ordering into a structured system connecting customers, vendors, and riders.

Why we built it

While staying in Miagao for university, I ran into the same problem many students there had. Ordering food usually meant Messenger chats, Facebook posts, and direct coordination: customers messaged vendors manually, vendors tracked orders through conversations, and riders coordinated deliveries separately. It worked, but it was informal, and as order volume grew, conversations got hard to manage. No clear order status, no centralized catalog, no reliable way to coordinate between the three sides. We built MiaGo to turn that into a proper delivery workflow.

How it turned out

MiaGo is a multi-role delivery platform with separate workflows for customers, vendors, and riders.

A system built around roles

Customers browse stores, manage carts, place orders, and track delivery progress. Vendors manage their stores and menu items and handle incoming orders. Riders view available delivery jobs, accept them, and update delivery status. Designing these separate experiences was one of the harder parts: every feature had to consider not just what users could do, but what they shouldn’t be allowed to do.

Modeling the delivery workflow

The hard part wasn’t the screens or endpoints. It was deciding how the system should behave in real situations: what happens if a customer cancels an order halfway through, a vendor runs out of an item someone already ordered, or two riders try to accept the same delivery. That pushed us past basic CRUD into designing controlled state transitions, where orders are processed with rules about who can change them and when.

Stores and ordering

Vendors create stores and manage their items; customers browse these catalogs directly instead of relying on scattered messages. When an order is placed, MiaGo stores the relevant details at that moment, so order history stays consistent even if menu items or prices change later.

Rider workflow

Riders use a job-based system where they see available deliveries and accept jobs. Once accepted, an order is locked to that rider to prevent conflicts.

How we built it

Technologies we used

Layer Stack
Frontend React, Vite, Tailwind CSS, shadcn/ui
Backend Express, TypeScript
Database PostgreSQL, Slonik
Validation Zod
Monorepo Turborepo
DevOps Docker, GitHub Actions
Deployment Vercel, Render, Neon

Backend architecture

MiaGo uses a modular monolith with separate modules for authentication, users, stores, orders, and deliveries, following a controller-service-repository structure that keeps business logic separate from database operations.

I built the database directly with PostgreSQL instead of an ORM, a deliberate choice: working with raw SQL gave me a better sense of how data was actually stored and queried. Slonik and Zod kept types consistent between the database and application.

Role-based access control

Authorization was one of the harder parts, since it wasn’t enough to just hide features from different users. Every action had to be checked against the user’s permissions and the order’s current state: a customer can’t modify a completed delivery, a rider can’t update an order they don’t own, a vendor only manages their own store. Keeping those boundaries consistent across backend and frontend took careful design.

What I learned

MiaGo taught me that software engineering is often about translating messy real-world processes into clear systems. The hardest problems weren’t technical so much as questions about how the real world should be represented in software: states, permissions, edge cases, how different people interact with the same data.

It also gave me deeper backend experience. Working with Express, raw SQL, and PostgreSQL made request flow and data handling much more visible than the higher-level frameworks I’d used before.