
FarmLink
A mobile agricultural marketplace backed by transactional APIs and PostgreSQL
A full-stack marketplace prototype connecting agricultural sellers and buyers through a Flutter client, RESTful Node.js services, PostgreSQL persistence, and managed product-media delivery.
The 15-second brief
At a glance
- Outcome
- Validated the complete marketplace path from role-aware onboarding and product media to catalog discovery and transaction-ready relational data.
- Delivery
- Prototype, 2025 · Coursework
- Focus
- Full-Stack, Mobile
- Scope
- 15 core technologies
- REST endpoints
- 48across 7 route modules
- Schema tables
- 77 enums, 21 indexes, 7 triggers
- Client screens
- 14~14k lines of Dart
Ownership and evidence
What I built
The product decisions and engineering work that shaped the final result.
Built a 48-endpoint REST API across seven route modules—users, farmers, buyers, products, cart, orders, and dashboard—with pagination, UUID, and payload validation applied as composable Express middleware rather than repeated inside handlers.
Modelled the marketplace on a seven-table PostgreSQL schema whose seven enum types encode the domain's fixed vocabularies—three roles, ten product categories, a seven-state order lifecycle, six payment methods—so invalid states are rejected by the database instead of by application checks.
Layered authorization as three separate JWT guards: role membership, resource ownership, and a farmer-product check that confirms the caller owns the product being modified, keeping the rule next to the route it protects.
Made checkout validate the entire cart before writing anything, returning requested-versus-available detail for every unavailable line rather than failing on the first one, then decrementing stock, flipping products to out-of-stock at zero, clearing the cart, and rolling up buyer and farmer totals.
Identified the checkout write path as non-transactional: the Supabase client exposes no transaction boundary, so a mid-sequence failure can decrement stock against a partially written order. The fix is to move the sequence into a Postgres function called as one RPC.
Structured the Flutter client as MVVM over repositories across 14 screens and roughly 14k lines of Dart—six view models, four repositories, seven services, 14 typed GoRouter routes, and 12 dependencies wired through get_it.
Uploaded product and profile images from the client straight to Cloudinary's REST API over multipart, behind a platform-neutral picked-image wrapper so web and native share a single upload path.
Product evidence
See it in action
Interface views and demonstrations from the working product.
Visual assets are not published for this project. The verified delivery and engineering scope are documented above.