← All projects
FastAPIReact NativeMongoDBRESTSystem Design

CampusCraves: food sharing without double-booking

A full-stack platform where students post surplus food, reserve items, and complete hand-offs — built by a 4-person team for a software engineering course. React Native on the front, FastAPI + MongoDB behind, and a reservation protocol designed so that two hungry students can never book the same sandwich.

20

REST endpoints across food, users, and reports routers

3-state

reservation lifecycle with atomic compare-and-set transitions

4-person

team; I built across backend routers and app screens

Architecture

An Expo/React Native app talks through a single API-client module to a FastAPI backend split into three routers, each owning one MongoDB collection. The backend runs on Render; the app consumed the deployed API throughout development.

Client

React Native + Expo

Marketplace, posting, reporting, NetID onboarding, notifications — all API calls funneled through apiService.js.

API

FastAPI routers

/api/food · /api/users · /api/report — Pydantic validation at every boundary.

Data

MongoDB

One collection per aggregate — food, users, reports — with conditional updates enforcing lifecycle invariants.

Domain model

User

  • netId · email · googleId?
  • role: user | admin
  • postCount · reservationCount

FoodPost

  • name · category · dietary info
  • pickup location · expiration
  • status · reservedBy · photo

Report

  • postId · reporter · reported
  • message
  • reviewStatus · reviewedBy
  • A User posts many FoodPosts, and reserves many others — both counted on their profile.
  • A Report links two Users through the FoodPost it concerns; a server-side guard blocks duplicate reports.
  • Admins review Reports through a pending → reviewed workflow with a reviewer audit trail.

The reservation lifecycle

Every food post carries a traffic-light status. The interesting part is what enforces it: each transition is a single conditional MongoDB update whose filter names the state it expects to leave.

/postAvailablestatus: “green/reserveatomic CASReservedstatus: “yellow/completeatomic CASCompletedstatus: “red
Each transition's MongoDB update filter includes the expected current status, so a lost race can never double-book an item.

When two users race for one item

The failure mode that motivated the design: two students tap “Reserve” at the same moment. Whoever's update matches first wins; the other request finds modified: 0, re-reads the item for an accurate error, and the app tells the truth.

UserApp (RN)API (FastAPI)MongoDBtap “Reserve”POST /api/food/reserve {food_id, user}update({_id, status:“green”}, → “yellow”)alt — update matched?modified: 1200 reserved“Reserved ✓”another user won the racemodified: 0 → re-check status400 already reserved
The race is resolved by the database, not the application: one conditional update decides the winner, and the loser gets an honest error instead of a silent overwrite.

Try it yourself

A live simulation of the reservation API, running the same invariants as the real backend. You're on the left; Maya is on the right. Reserve something, complete a pickup — or hit the race button and watch the server let exactly one of you win.

CampusCraves

You · kn2200

Veggie wrap

Available

📍 D2 Dining Hall

Chocolate croissants ×3

Available

📍 Library Café

Chicken biryani

Available

📍 A5 Kitchen

CampusCraves

Maya · mp5510

Veggie wrap

Available

📍 D2 Dining Hall

Chocolate croissants ×3

Available

📍 Library Café

Chicken biryani

Available

📍 A5 Kitchen

SERVER LOG — the conditional update decides every request

Waiting for requests…

What this shows

  1. 1

    Push invariants into the database.

    The no-double-booking rule lives in the update filter itself — the one place a race can't slip past — rather than in client checks or read-then-write logic.

  2. 2

    Model the domain as a small state machine.

    Three named states with explicit transitions made authorization rules simple to state and test: only the reserver may complete, only available items may be reserved.

  3. 3

    Team-scale engineering practices.

    Feature-branch workflow across a 4-person team, a pytest suite over every router, and a deployed backend (Render) the mobile app consumed throughout development.

Architecture docs, API reference, and the full codebase live in the repo.

komalnpn/campus-craves →