Rental Heatmap Report - Azure CosmosDB Integration
Rental Heatmap Report - Azure CosmosDB Integration
Overview
The Rental Market Heatmap Report has been successfully integrated with Azure CosmosDB rental listing data, providing real-time market analysis based on actual property listings.
Integration Architecture
Azure CosmosDB
↓
Supabase (properties table)
↓
API: /api/rental-listings
↓
Heatmap Report Page
↓
Report Visualization
Data Flow
1. Data Sync from Azure
Azure listings are synced to Supabase's properties table with the following fields:
Relevant Fields for Heatmap:
price→ Monthly rent amountbeds→ Number of bedroomscity→ City locationstate→ State (default: Oregon)zip_code→ ZIP codeproperty_manager→ Property manager nameavailability→ Current availability statusmarket_status→ Market status
Additional Metadata:
property_name/address- Property identificationbaths,sqft- Property detailsazure_listing_id- Reference to original Azure listinglast_synced_at- Sync timestamp
2. API Endpoint: /api/rental-listings
Location: app/api/rental-listings/route.ts
Method: GET
Query Parameters:
city(optional) - Filter by city name (case-insensitive)state(optional) - Filter by state (default: OR)minPrice(optional) - Minimum monthly rentmaxPrice(optional) - Maximum monthly rent
Response:
{
"success": true,
"count": 150,
"listings": [
{
"id": "uuid",
"unitType": "1BR",
"bedrooms": 1,
"monthlyRent": 1200,
"zipCode": "97203",
"neighborhood": "Portland",
"acceptsVoucher": false,
"address": "123 Main St",
"propertyManager": "ABC Property Management",
"availability": "Available"
},
// ... more listings
]
}
Data Transformation:
The API automatically converts database fields to the RentalListing interface:
{
id: property.id,
unitType: formatUnitType(property.beds), // 0 → Studio, 1 → 1BR, etc.
bedrooms: property.beds,
monthlyRent: property.price,
zipCode: property.zip_code,
neighborhood: property.city,
// Additional context
address: property.address,
propertyManager: property.property_manager,
availability: property.availability,
}
3. Heatmap Report Page
Location: app/access-crm/reports/rental-heatmap/page.tsx
Features:
✅ Real Data Integration
- Fetches live data from Azure via API
- Defaults to real data on load
- Falls back to sample data if no listings found
✅ Data Source Toggle
- 🟢 Real Data - Pulls from Azure CosmosDB
- 🔵 Sample Data - Uses generated demo data
- Visual indicator showing which source is active
✅ Filtering Capabilities
- Filter by city (e.g., "Portland")
- More filters can be added (state, price range, etc.)
- Real-time report regeneration on filter change
✅ Error Handling
- Graceful fallback to sample data on error
- Clear error messages with recovery options
- Warning banners when using fallback data
✅ Data Source Display
Shows exactly where data is coming from:
- "Azure CosmosDB (150 listings)"
- "Sample Data (No Azure listings found)"
- "Sample Data (Demo)"
Usage Examples
Basic Usage (Default)
Navigate to /access-crm/reports/rental-heatmap
The report will automatically:
- Fetch Oregon listings from Azure
- Generate heatmap with real data
- Display market statistics
- Show affordability analysis
Filtered by City
// In the UI: Enter "Portland" in the City filter
// API call made:
GET /api/rental-listings?state=OR&city=Portland
// Result: Report shows only Portland listings
Switch to Sample Data
Click the "🔵 Sample Data" toggle button to see demo data for testing/comparison purposes.
Implementation Details
API Route (app/api/rental-listings/route.ts)
// Fetch properties with valid pricing
let query = supabase
.from('properties')
.select(/* fields */)
.not('price', 'is', null)
.gt('price', 0);
// Apply filters
if (city) query = query.ilike('city', `%${city}%`);
if (state) query = query.eq('state', state);
// Transform to RentalListing format
const listings = properties.map(property => ({
id: property.id,
unitType: formatUnitType(property.beds),
monthlyRent: property.price,
// ... more fields
}));
Unit Type Formatting
function formatUnitType(beds: number | null): string | undefined {
if (beds === 0) return 'Studio';
if (beds === 1) return '1BR';
if (beds === 2) return '2BR';
if (beds === 3) return '3BR';
if (beds >= 4) return '4BR+';
return undefined;
}
Report Generation
// Fetch from API
const response = await fetch('/api/rental-listings?state=OR&city=Portland');
const data = await response.json();
// Generate report
const report = generateRentalHeatmapReport(data.listings, {
region: 'Oregon Market Area',
reportingPeriodStart: thirtyDaysAgo,
reportingPeriodEnd: now,
});
Data Validation
The API ensures data quality:
✅ Required Fields:
pricemust not be nullpricemust be greater than 0
✅ Optional Fields:
- Missing
beds→ defaults to undefined (normalized to 1BR) - Missing
city→ defaults to "Unknown" - Missing
zip_code→ undefined (optional in report)
✅ Transformations:
- Bedrooms → Unit Type conversion
- Database snake_case → camelCase
- Null handling with safe defaults
Performance Considerations
Optimizations Implemented:
-
Batch Fetching
- Automatically fetches in batches of 1000
- Handles unlimited number of listings
- No pagination limit
- Progress logging for large datasets
-
Indexed Queries
- Filter by
price IS NOT NULL - Filter by
state(indexed column) - City uses
ilike(case-insensitive)
- Filter by
-
Selective Fields
- Only fetch required columns
- Reduces payload size
- Faster query execution
-
Client-Side Caching
- React state management
- Re-fetch only on filter change
- Efficient re-rendering
Expected Performance:
- < 1s - Fetch 100-500 listings
- 1-3s - Fetch 500-2000 listings
- 3-5s - Fetch 2000-5000 listings
- 5-10s - Fetch 5000+ listings
Note: The API automatically handles large datasets by fetching in batches of 1000 records, ensuring all listings are included regardless of total count.
Monitoring & Debugging
Check Data Source
Look for the data source indicator in the UI:
Source: Azure CosmosDB (150 listings)
API Debugging
# Test API directly
curl http://localhost:3000/api/rental-listings?state=OR
# With filters
curl "http://localhost:3000/api/rental-listings?state=OR&city=Portland"
Console Logging
The page logs useful information:
console.log('Fetching rental listings from database...');
console.warn('No real data available, falling back to sample data');
console.error('Error generating report:', error);
Future Enhancements
Planned Features:
-
Additional Filters
- Price range slider
- Property type filter
- Availability status filter
- Voucher acceptance filter
-
Caching Layer
- Redis cache for API responses
- Configurable TTL
- Cache invalidation on sync
-
Real-Time Updates
- WebSocket connection
- Live data refresh
- Notification on new listings
-
Advanced Analytics
- Historical trends
- Seasonal patterns
- Comparative analysis
-
Export Options
- PDF report generation
- CSV data export
- Email scheduling
Troubleshooting
Issue: "No Azure listings found"
Possible Causes:
- No properties synced yet
- All properties have null/zero prices
- Filters too restrictive
Solutions:
# Check if properties exist
SELECT COUNT(*) FROM properties WHERE price > 0;
# Run sync if needed
# Navigate to /access-crm → Sync Properties
Issue: API returns 500 error
Check:
- Supabase connection
- Environment variables
- Server logs
Debug:
// In route.ts, add logging
console.log('Fetching properties:', { city, state });
console.log('Query result:', data);
Issue: Report shows wrong data
Verify:
- Data source indicator (Real vs Sample)
- Active filters
- Browser console for errors
Testing
Test Real Data Integration
- Navigate to
/access-crm/reports/rental-heatmap - Ensure "🟢 Real Data" is selected
- Check data source shows "Azure CosmosDB"
- Verify listing count matches database
Test Filters
- Click "Filters" button
- Enter city name (e.g., "Portland")
- Verify report regenerates
- Check listing count decreases appropriately
Test Error Handling
- Disconnect from database (simulate error)
- Verify graceful fallback to sample data
- Check error message displays
- Verify "Switch to Sample Data" button works
Configuration
API Defaults
// Default query parameters
const DEFAULT_STATE = 'OR';
const DEFAULT_MIN_PRICE = 0;
const DEFAULT_ORDER = 'created_at DESC';
Report Defaults
// Default region
const DEFAULT_REGION = 'Oregon Market Area';
// Default date range
const REPORT_PERIOD_DAYS = 30;
Summary
✅ Fully Integrated with Azure CosmosDB ✅ Real-Time Data from Supabase properties table ✅ Flexible Filtering by city, state, price ✅ Error Resilient with graceful fallbacks ✅ User-Friendly toggle between real and sample data ✅ Well-Documented API and implementation
The integration is complete and production-ready! Users can now generate market heatmap reports using actual rental listing data from Azure CosmosDB.
Last Updated: November 24, 2025
Status: ✅ Complete and Production Ready
Data Source: Azure CosmosDB → Supabase → API → Heatmap
Use links in each imported doc to open its source.