Property Marketplace Platform
In DevelopmentGharBazaar
Deployed and running — pre-launch, so the catalogue is still small
Status Note: The platform is deployed and running at gharbazaar-mu.vercel.app, with the database connected and every marketplace route live. It is pre-launch rather than finished: seller onboarding is still in progress, so the catalogue holds only a few listings and no student listings yet. The build-status table below marks exactly what is complete.
Overview
Project Overview
GharBazaar is a property marketplace built around a single idea: students looking for housing care about distance to their campus, not postal boundaries. Properties are linked to nearby universities with distances computed in the database using PostGIS, so a student picks their university and sees genuinely nearby flats, PGs and hostels ranked by real distance. Around that sits a full marketplace — tiered identity verification, listing moderation, lead capture with contact gating, in-app messaging, subscriptions and billing.
Screenshots
What it looks like
Captured from the running product — not mockups. You can check any of it against the live site — deployed and running — pre-launch, so the catalogue is still small.



On a phone

Not shown here (8)
- University selection to distance-ranked resultsNo property is linked to a university yet — the listings are in Mathura, the universities in NCR, so there is no real distance ranking to show
- Map view with pins near a campusNeeds listings with campus links to be meaningful
- Owner listing creation and university taggingRequires a seller account; not captured to avoid creating listings on the live site
- Three-tier KYC submission and reviewWould mean submitting real identity documents
- Leads dashboard showing contact-reveal stateRequires a seller account with real leads
- In-app messaging threadWould mean sending real messages between real accounts
- Admin moderation queueRequires an admin account
- CI run showing the pipeline greenThe lint fix is committed locally but not yet pushed, so the remote pipeline is still red
Problem
The Problem
A student moving to a new city has a housing search that no general property portal serves. The big portals index by locality and price, but a student's real constraint is how far a place is from their campus — and locality names carry no distance information. The informal alternatives, campus WhatsApp groups and noticeboards, have no verification at all, which is exactly where students get scammed. Property owners near universities have the opposite problem: no targeted way to reach student tenants. GharBazaar makes campus proximity a first-class, computed property of every listing, and puts a verification system behind the people posting them.
Role
What I Built
- ↗Designed the complete product architecture and the 58-table PostgreSQL schema
- ↗Modelled property-to-university proximity with PostGIS, computed by database triggers
- ↗Wrote 128 Row-Level Security policies covering every table in the schema
- ↗Built the three-tier KYC verification flow with private document storage
- ↗Built the property lifecycle: submission, admin approval, publication and automated expiry
- ↗Built the lead system with contact-reveal gating to protect student privacy
- ↗Implemented a transactional outbox for notifications, drained by a scheduled job
- ↗Built saved searches with a nightly alert-matching job
- ↗Built the multi-role architecture spanning students, sellers, property dealers and admins
- ↗Built the admin surface: moderation queue, KYC review, reports, strikes and support tickets
- ↗Wrote 196 Vitest unit tests and 23 Playwright end-to-end specs, wired into a GitHub Actions pipeline
Architecture
System Architecture
GharBazaar is a Next.js 16 App Router application over Supabase PostgreSQL. Business logic lives in feature modules with server-only data access, exposed through 59 route handlers under /api/v1. Authorization is enforced primarily in the database: 128 Row-Level Security policies cover all 58 tables, so a compromised or buggy route cannot read data its caller shouldn't see. Geospatial relationships are computed by PostGIS inside database triggers rather than in application code, which keeps distances correct no matter which code path writes a property.
Select a university → browse nearby verified listings ranked by real distance
List properties → complete KYC → manage leads and appointments
Moderate listings, review KYC, resolve reports, issue strikes, handle support
Next.js 16 App Router · React 19 · Tailwind v4 · shadcn · Base UI
59 route handlers under /api/v1 over server-only feature modules
Supabase PostgreSQL — 58 tables, 67 indexes, 16 functions, 21 triggers
128 Row-Level Security policies — the database is the final authority
PostGIS geography(point) with st_distance computed in triggers
Listing expiry · saved-search alert matching · notification outbox drain
Key Architectural Decisions
- ·University proximity as computed data, not user input — distance is written by a trigger and cannot be inflated by a seller
- ·Row-Level Security as the primary authorization boundary rather than route-level checks alone
- ·Tiered verification (identity → property → trusted) instead of a single binary verified flag
- ·Transactional outbox for notifications so a delivery failure never rolls back the business action
- ·Contact details revealed only after an explicit lead, keeping student contact data out of listing pages
- ·Soft delete enforced at the database — a property cannot be hard-deleted out from under its audit trail
Build Status
What's Built vs In Progress
This project is in active development. The table below shows the current implementation status of each system area.
Technical Breakdown
System-by-System Engineering
A detailed look at each layer and subsystem — decisions made, how it was built, and why it was built that way.
Geospatial proximity model
CompletedThe feature the whole product rests on. Properties and universities both carry a PostGIS geography point, and the distance between a linked pair is computed by a database trigger — never supplied by a user.
- ·PostGIS extension enabled; properties and universities both store geography(point) locations
- ·property_universities join table with a composite primary key and a computed_distance_m column
- ·A trigger computes st_distance on link creation; a second trigger recomputes every linked distance when a property's pin moves
- ·Distance is explicitly never user input — a seller cannot claim to be closer to a campus than they are
- ·Composite index on (university_id, computed_distance_m) so 'nearest first' is an index scan
- ·Google Maps integration for pin placement and map-based browsing
Database & authorization
CompletedA 58-table schema where authorization is a database concern. Row-Level Security covers every table, and the migration history shows security being tightened deliberately over time.
- ·58 tables, 67 indexes, 16 functions and 21 triggers across 8 migrations
- ·128 Row-Level Security policies, with RLS enabled on all 58 tables
- ·Dedicated hardening migrations for RLS gaps, financial record protection and view-counter abuse
- ·Hard deletion of properties blocked at the database, preserving audit history
- ·Separate audit_logs and activity_logs tables for administrative and user actions
- ·Security-definer functions used deliberately and scoped with an explicit search_path
Identity & verification
CompletedTrust is the core problem in a housing marketplace. Verification is tiered rather than binary, so the platform can distinguish a verified identity from a verified property.
- ·Three verification levels: identity, property, and trusted
- ·Verification request states: pending, approved, rejected, expired
- ·KYC documents stored in a private bucket with MIME-type and file-size restrictions
- ·Admin endpoints to verify or reject a submission
- ·Property images kept in a separate public bucket — private documents never share a bucket with public assets
Listing lifecycle
CompletedProperties move through submission, moderation and publication, and expire automatically so the catalogue does not fill with stale listings.
- ·Admin approve and reject endpoints backing a moderation queue
- ·Separate publish action, so approval and going live are distinct steps
- ·Automated daily expiry job for listings past their active window
- ·Price history retained per property
- ·Stable slug management so a renamed listing keeps a working URL
- ·Amenities, property types, room types and areas maintained as seeded reference data
Leads & communication
CompletedStudent contact details are never exposed on a listing. A lead is an explicit act, and only then does contact information become visible.
- ·Inquiry model capturing student interest against a property
- ·Contact reveals recorded separately, so who saw which details is auditable
- ·In-app conversations and messages between parties
- ·Visit appointment scheduling
- ·Lead export endpoint for sellers working their pipeline outside the platform
- ·Engagement events captured across eight types: view, gallery open, reveal, WhatsApp click, call click, share, favourite and visit request
Notifications
CompletedNotifications use a transactional outbox. A notification is written in the same transaction as the action that caused it, then delivered asynchronously by a scheduled job.
- ·notification_outbox table written as part of the originating transaction
- ·A scheduled job drains the outbox on a daily cadence
- ·Delivery failure cannot roll back or lose the business action that triggered it
- ·Per-user notification feed with read-state tracking
Search & discovery
CompletedSearch combines university proximity with the ordinary filters a renter needs, and remembers searches so students hear about new matches.
- ·Dedicated search feature module with a student-housing entry path
- ·Filtering across property type, room type, amenities and area
- ·Saved searches persisted per user
- ·A nightly alert-matching job pairs new listings against saved searches
- ·Search events recorded for relevance and zero-result analysis
- ·Favourites, comparison sets and recently-viewed lists per user
Monetization
CompletedSellers and dealers subscribe to plans; the platform tracks payments, issues invoices and consumes provider webhooks.
- ·Subscription plans and per-user subscriptions
- ·Payment records with invoices
- ·Webhook event table for reconciling provider callbacks
- ·A dedicated migration protecting financial records from modification
Trust, safety & admin
CompletedA marketplace needs enforcement as much as features. Reports, strikes and blocks are first-class, and admins have a purpose-built surface.
- ·Property reports with resolve and dismiss actions
- ·Progressive enforcement via user strikes and account deactivation
- ·User blocks and blocked identifiers to stop repeat abuse
- ·Support ticket system with threaded admin replies and status transitions
- ·Reference-data seeding endpoints for cities, areas, universities, amenities and property types
- ·19 dedicated admin endpoints out of 59 total route handlers
Quality & delivery
CompletedThe project has a unit test suite and a CI pipeline wired to run it. The tests pass locally; the pipeline itself is currently red at the lint step, so the later stages have not yet run green.
- ·196 Vitest unit tests covering schemas, storage, formatting and API invariants — all passing locally
- ·23 Playwright end-to-end specs — 10 run against the deployed app; the other 13 self-skip without service-role credentials, so they never write to live data
- ·Coverage reporting configured
- ·GitHub Actions pipeline: install, lint, typecheck, unit tests, build, then end-to-end tests
- ·The lint failures that were blocking CI are fixed; the change is committed but not yet pushed, so the remote pipeline is still red
- ·SEO infrastructure with robots, dynamic sitemaps and structured data
- ·Vercel Analytics and Speed Insights instrumented
- ·Image upload pipeline with client-side compression, cropping and a media job queue
Challenges
Engineering Challenges & Solutions
Challenge
Making campus distance a fact rather than a claim
The entire product promise is that a listing really is close to a campus. If a seller types in the distance, or the application computes it on write, the number drifts the moment a property's pin is corrected — and a wrong distance is worse than none, because students plan around it.
Solution
Distance is derived data owned by the database. Both properties and universities store a PostGIS geography point, and a trigger computes st_distance whenever a property-university link is created. A second trigger recomputes every affected link when a property's location changes. The column is annotated in the schema as always trigger-computed and never user input, and it is indexed alongside university id so proximity ordering stays cheap.
Challenge
Authorization across four roles and 58 tables
Students, sellers, dealers and admins have overlapping but distinct access to the same tables. Enforcing that only in route handlers means every new endpoint is a fresh chance to leak a phone number or someone else's KYC document.
Solution
Authorization was pushed into the database. All 58 tables have Row-Level Security enabled, covered by 128 policies, so access rules live next to the data rather than being re-implemented per route. The migration history includes dedicated hardening passes — closing RLS gaps, protecting financial records and restricting an abusable view-counter function — which is how those rules got tightened without guesswork.
Challenge
Notifications that cannot silently disappear
Sending a notification inline with the action that triggers it couples them: if the delivery call fails or times out, either the user's action fails or the notification is lost. Neither is acceptable when the notification is 'your listing was approved'.
Solution
Notifications are written to an outbox table inside the same transaction as the originating action, then delivered asynchronously by a scheduled job that drains the outbox. If the business action commits, the notification is durably queued; if delivery fails, it is retried without ever having risked the action itself.
Challenge
Protecting student contact details from cold outreach
If contact details sit on the listing page, students get called by every owner who scrapes the site, and the platform loses both trust and any ability to measure real intent.
Solution
Listings expose property information only. Contact details become available through an explicit inquiry, and each reveal is recorded in its own table so it is auditable and countable. That keeps student data out of public pages, gives sellers higher-intent leads, and turns 'reveal' into a measurable funnel step alongside views and gallery opens.
Decisions
Technical Decisions
Key technology and architecture choices — what was chosen, what was considered, and why.
Decision — Where geospatial distance is computed
Chosen
PostGIS in database triggers
Considered
Application-side distance calculation on read
Reason
Computing on read means every query recalculates, and every code path that writes a location has to remember to update. A trigger makes distance a guaranteed property of the data and lets it be indexed, so nearest-first ordering is an index scan rather than a sort over computed values.
Decision — Primary authorization boundary
Chosen
Row-Level Security in PostgreSQL
Considered
Route-level permission checks only
Reason
With 59 route handlers and four roles, per-route checks scale badly and fail open when one is forgotten. RLS puts the rule next to the data, so a missed check in a handler results in an empty result set rather than someone else's KYC documents.
Decision — Verification model
Chosen
Three tiers — identity, property, trusted
Considered
A single verified boolean
Reason
A binary flag cannot express the difference between 'we know who this person is' and 'we have confirmed they control this property'. Tiering lets the platform surface an honest trust signal to students and gives sellers a reason to complete each step.
Decision — Notification delivery
Chosen
Transactional outbox drained by a scheduled job
Considered
Sending inline with the triggering action
Reason
Inline delivery couples a business transaction to a third-party call. The outbox pattern decouples them — the notification is committed atomically with the action and delivered independently, so a provider outage delays notifications instead of failing approvals.
Decision — Schema management
Chosen
Versioned SQL migrations on Supabase
Considered
An ORM-managed schema
Reason
The schema leans on PostgreSQL features an ORM abstracts away or cannot express — PostGIS geography columns, Row-Level Security policies, security-definer functions and triggers. Writing SQL directly keeps those first-class and keeps every policy reviewable in the migration history.
Deployment
Deployment & Infrastructure
The landing site is live at gharbazaar.in. The marketplace application itself is built and CI-verified but has no production deployment yet — that is the current focus.
- ·Landing site deployed and live at gharbazaar.in
- ·Deployed on Vercel with the database connected; seller onboarding is the remaining pre-launch work
- ·Deployment configuration committed, including three scheduled jobs for listing expiry, alert matching and outbox draining
- ·GitHub Actions runs lint, typecheck, unit tests, a production build and end-to-end tests on every push — the pipeline is currently failing at lint
- ·Supabase provides managed PostgreSQL, authentication and storage; migrations are versioned in the repository
- ·Vercel Analytics and Speed Insights instrumented ahead of launch
Learnings
What I Learned
Next Steps
What I Would Build Next
- →Get the CI pipeline green — it fails at lint, which blocks the test and build stages
- →Onboard sellers so the catalogue and student listings fill out
- →Publish seeded demo accounts for each role so the platform can be explored without signing up
- →Full-text and typo-tolerant search across listings
- →Native mobile experience — student housing search is overwhelmingly mobile
- →Expand end-to-end coverage across the seller and admin journeys
- →Landlord analytics dashboard built on the existing listing-event data