• 5 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 experienced the same problem many students there had.

When ordering food, the process usually happened through Messenger chats, Facebook posts, and direct coordination. Customers had to message vendors manually, vendors had to track orders through conversations, and riders had to coordinate deliveries separately.

The system worked, but it was informal.

As the number of orders increased, conversations became difficult to manage. There was no clear order status, no centralized catalog, and no reliable way to coordinate between customers, vendors, and riders.

We built MiaGo to turn that informal process into a proper delivery workflow.

How it turned out

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

A system built around roles

The biggest difference between MiaGo and a simple ordering app is that every user has a specific role in the workflow.

Customers browse stores, manage their carts, place orders, and track delivery progress.

Vendors manage their stores and menu items while handling incoming orders.

Riders act as the connection between both sides by viewing available delivery jobs, accepting orders, and updating delivery status.

Designing these separate experiences was one of the harder parts of the project. Every feature had to consider not only what users could do, but also what they should not be allowed to do.

Modeling the delivery workflow

The hardest part was not creating the screens or endpoints. It was deciding how the system should behave in real situations.

Real orders are not always straightforward.

What happens if a customer cancels an order halfway through? What happens if a vendor runs out of an item after someone already ordered it? What happens if two riders try to accept the same delivery?

These questions forced us to think beyond basic CRUD operations and design controlled state transitions.

Orders became more than just records in a database. They became processes with rules about who can change them and when those changes are valid.

Stores and ordering

Vendors can create stores and manage their available items.

Customers can browse these catalogs directly instead of relying on scattered messages to know what is available.

When an order is placed, MiaGo stores the relevant details at that moment, keeping the order history consistent even if menu items or prices change later.

Rider workflow

Riders use a job-based system where they can see available deliveries and accept jobs.

Once a rider accepts an order, it becomes assigned to them to prevent conflicts.

This converts delivery from an informal agreement into a structured workflow where responsibilities are clear.

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 architecture with separate modules for authentication, users, stores, orders, and deliveries.

The backend follows a controller-service-repository structure, keeping business logic separate from database operations.

The database was built directly with PostgreSQL instead of using an ORM.

This was an intentional choice. Working directly with SQL gave me a better understanding of how data was actually being stored and queried instead of relying on abstractions.

Type safety was maintained through Slonik and Zod, which helped ensure that data moving between the database and application stayed consistent.

Role-based access control

The three user roles made authorization one of the more challenging parts of the system.

It was not enough to simply hide features from different users. Every action had to be checked against the user’s permissions and the current state of the order.

A customer should not be able to modify a completed delivery. A rider should not be able to update an order they do not own. A vendor should only manage their own store.

Keeping these boundaries consistent across both the backend and frontend required 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 were questions about how the real world should be represented in software.

Building a delivery system forced me to think about states, permissions, edge cases, and how different people interact with the same data.

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