Mudassir
All Projects

Foodtopia

A trust-weighted restaurant discovery app where credibility shapes what you see.

RoleSolo Developer
Timeline2025
Stack
React NativeExpoTypeScriptFastifyPostgreSQLPrismaZodJWTArgon2CloudinaryExpo SecureStoreReact NavigationReact Native ReanimatedExpo LocationGitHub Actions

The Challenge

The core challenge was designing a trust system that is both mathematically meaningful and resistant to gaming. The credibility score (0-100) is a weighted composite of account age, review volume, upvote ratio, follower count, recent activity, and verified status. Critically, votes themselves are weighted by the voter credibility — a verified critic upvote contributes 2.5x more than a new account. This creates a self-reinforcing quality loop without requiring manual moderation. On the mobile side, building a real-time social feed across iOS and Android with optimistic UI updates, location-aware discovery, and multi-photo uploads via Cloudinary — while maintaining a smooth glass-morphism aesthetic with blur layers and gradients — required careful performance tradeoffs.

Architectural Decisions

verified

Credibility-weighted voting engine

Instead of treating all votes equally, each vote carries a multiplier (0.5x-2.5x) based on the voter credibility score. This was implemented in a dedicated credibility.ts service that recomputes scores on vote events. The alternative — equal-weight votes — was rejected because it makes the platform trivially gameable.

speed

Fastify with Prisma over REST convention

Fastify was chosen over Express for its schema-based performance characteristics and native TypeScript support. Combined with Prisma type-safe query builder, every API response is validated end-to-end: Zod validates incoming payloads, Prisma enforces schema correctness, and TypeScript catches type mismatches at compile time.

cloud_upload

Cloudinary for image uploads at the edge

Rather than routing photo uploads through the backend, photos are uploaded directly from the mobile client to Cloudinary using a public upload preset. The backend only stores the returned URL. This offloaded resizing, CDN distribution, and format conversion to a specialized service.

share

React Context API over Redux or Zustand

Global state is deliberately minimal — only auth tokens and saved restaurant IDs need cross-screen access. Using Context API with hooks avoided the boilerplate overhead of Redux for a scope that did not warrant it. SavedContext uses optimistic updates to make saves feel instant.

history

Prisma migrations as living schema documentation

Nine incremental migrations capture the product evolution — from basic auth, through restaurant reviews and votes, to a full social graph with follows, comments, lists, and achievements. Each migration is a readable artifact of product decisions.

emoji_events

Achievement system with credibility thresholds

Achievements are evaluated in a dedicated achievements.ts service against the user current stats. Rather than triggering achievements via database triggers, they are computed server-side on relevant events. This keeps the logic testable, readable, and easy to extend.

Impact

40+
API endpoints across 10 route modules
13
database models with 9 schema migrations
5,700+
lines of TypeScript across frontend and backend
2.5x
max vote weight for verified critics vs. new users
Next Project

Life OS