LodgesAndCaravans UK Property Listing & Sales Platform
Full-stack property listing and sales marketplace for the UK holiday property market, built with Next.js SSR, geospatial map search, and owner management dashboards.
Sole Full-Stack Developer (End-to-End Delivery)
5 Months
Lodges & Caravans
Full-Stack Web


The Problem & Engineering Constraints
Why this system needed to be built
The platform needed to render and filter hundreds of property listings scattered across the entire UK on a single interactive map view in real time. Naive approaches caused severe rendering lag from overlapping markers, redundant API calls on every pan/zoom, and full-collection MongoDB scans that ignored the user's visible map area. The core engineering challenge was building a system where the map, filters, and listing results stayed perfectly synchronized without sacrificing load speed or scalability.
UK property buyers browsing accommodation, caravan and lodge owners managing property listings, and site administrators.
Real-World Constraints
Non-Negotiables- Sole engineer ownership: Responsible for the entire lifecycle from database architecture and auth to frontend SSR, SEO optimization, and deployment.
- Sub-second page load requirement: Dynamic holiday listing pages needed instant initial render times to maximize conversion from UK organic search traffic.
- Geospatial query accuracy: Searches needed to handle UK postcodes, radius boundaries, and multi-parameter property filtering simultaneously.
*Every architecture decision was framed strictly around these constraints.
Engineering Challenges & Root-Cause Fixes
Real technical hurdles encountered during production development and how they were solved under the hood.
Map Rendering Collapse with Hundreds of UK-Wide Pins
Loading the full UK map view with 170+ property markers caused visible frame drops, overlapping pin labels, and 3-4 second interaction freezes on mobile devices.
Every property was rendered as an individual DOM-based marker. The browser was thrashing layout recalculations on pan/zoom with hundreds of absolutely-positioned elements.
Implemented Google Maps MarkerClusterer with density-aware grid sizing, lazy marker instantiation outside the visible viewport, and debounced cluster recalculation on zoom-end events. Required tuning cluster grid size and max zoom thresholds per UK region density to balance rural vs. dense coastal parks.
Eliminated rendering lag entirely, achieving smooth panning across the full UK map with 170+ active listings and zero visible marker overlap.
Redundant API Calls on Every Map Pan and Zoom
Each map drag or zoom level change fired a fresh API request, causing visible loading spinners, network waterfall congestion, and unnecessary MongoDB load.
No client-side caching strategy existed, so every viewport change was treated as a unique, uncached request.
Introduced TanStack Query with query keys hashed from rounded viewport bounds and active filter state. Stale-while-revalidate serves previously fetched regions instantly while background refetches sync silently. Cache key granularity was tuned to avoid stale results on broad keys without killing hit rate on narrow viewport shifts.
Reduced redundant API calls by ~70% during typical browse sessions; revisiting previously viewed UK regions loads instantly from cache.
Full-Collection MongoDB Scans Ignoring Visible Map Area
Property search API was querying the entire UK listings collection regardless of what portion of the map the user was actually viewing, spiking response times to 1,400ms+ under concurrent load.
The original query used $nearSphere with a large radius instead of scoping to the map's actual bounding box. Combined with poorly ordered compound index keys, MongoDB fell back to in-memory filter scans.
Refactored the API to accept the map's northeast/southwest bounding coordinates and query with $geoWithin polygon. Restructured compound 2dsphere indexes to place geospatial fields first, followed by faceted filter keys. Added a Redis cache layer for hot geographic corridors.
Reduced viewport-scoped query times from 1,400ms to under 95ms; Redis serves repeat corridor queries in <5ms.
Key Features & Implementation Mechanics
UK-Wide Map Search with Marker Clustering
Browse all UK properties on a single interactive map with Google Maps marker clustering that dynamically groups and splits pins as users zoom in and out.
Google Maps MarkerClusterer with density-aware grid sizing, viewport-bound MongoDB $geoWithin queries scoped to visible bounding box, and TanStack Query stale-while-revalidate caching keyed on rounded viewport bounds.
Owner Property & Sales Management Portal
Full-featured owner dashboard to upload gallery images, manage pricing, track buyer inquiries, and view visitor analytics.
Role-based JWT authentication with secure multipart image uploads, automated thumbnail compression, and MongoDB transaction pipelines.
Viewport-Synced Multi-Attribute Filtering
Filter properties across price, capacity, pet rules, amenities, and regions, keeping results synchronized with the visible map area in real time.
URL-driven state management with Next.js shallow routing, debounced viewport-bound API calls, Redis-cached hot corridor results, and compound 2dsphere + faceted MongoDB indexes.
Verified Metrics & Telemetry
Viewport Query Speed
Reduced from 1,400ms via viewport-bound 2dsphere indexing and Redis caching for hot geographic corridors.
Redundant API Calls Eliminated
TanStack Query stale-while-revalidate caching prevents re-fetching previously viewed map regions.
Redis Cache Hit Latency
Popular search corridors (Cornwall, Devon, Lake District) served from Redis without MongoDB roundtrips.
Lessons Learned & Architectural Evolution
What Worked Well & Enduring Takeaways
- ✓Compound index key ordering in MongoDB makes or breaks geospatial performance when paired with faceted filters.
- ✓Single-engineer delivery succeeds by adhering to high-leverage primitives (Next.js SSR, clean REST contracts, automated image compression) rather than over-engineering microservices.
What I Would Build Differently Today
- ↺If architecting today, I would implement Meilisearch or Typesense for typo-tolerant location search and instant facet count calculations.
- ↺I would use Next.js Parallel Routes for modal-based listing previews to preserve search scroll position on mobile devices.