How to Migrate from Deprecated RETS/IDX to External Feeds in PropertyWebBuilder
PropertyWebBuilder removed its legacy RETS/IDX connector in December 2024 and replaced it with a provider-agnostic ExternalFeed::Manager system that normalizes third-party APIs like Resales Online behind a unified Ruby interface.
In December 2024, the PropertyWebBuilder open-source real-estate platform deprecated its tightly-coupled RETS/IDX integration in favor of a flexible external-feed architecture. This migration guide walks you through transitioning from the old MlsConnector class to the new ExternalFeed::Manager façade, referencing the actual source code in the etewiah/property_web_builder repository to ensure your property website continues pulling listings without interruption.
Why RETS/IDX Was Removed
The legacy connector located at app/services/pwb/mls_connector.rb was officially removed in December 2024 (see the deprecation notes in lines 4-7). The old system relied on rigid MLS protocols that were difficult to extend for modern REST APIs or GraphQL feeds. The new external-feed architecture abstracts any third-party data source behind a consistent, testable Ruby interface while maintaining the same public API methods (search, find, similar, locations, and property_types).
Core Architecture Components
ExternalFeed::Manager
The ExternalFeed::Manager class in app/services/pwb/external_feed/manager.rb (lines 5-30) serves as the central façade that controllers, view helpers, and background jobs interact with. It handles configuration validation, parameter normalization, caching, and error handling before delegating to concrete providers.
Provider Registry and BaseProvider
The Provider Registry in app/services/pwb/external_feed/registry.rb (lines 3-12) maps symbolic provider names (e.g., :resales_online) to concrete implementation classes. New providers register themselves here without touching core business logic.
All providers inherit from BaseProvider, an abstract class that enforces a strict contract requiring implementations of search, find, similar, locations, property_types, and available?. The reference implementation for Resales Online resides in app/services/pwb/external_feed/providers/resales_online.rb (lines 10-70), demonstrating how to translate internal search parameters into provider-specific query formats and normalize JSON responses into NormalizedProperty objects.
Configuration Storage
External feed settings are stored in three JSON columns on the pwb_websites table, as defined in app/models/pwb/website.rb:
external_feed_enabled(boolean) – toggles the feature on/offexternal_feed_provider(string) – stores the provider symbol (e.g., "resales_online")external_feed_config(hash) – contains provider-specific secrets likeapi_keyandapi_id_sales
Step-by-Step Migration Guide
1. Enable the External Feed Feature
Navigate to the Site-Admin UI at /site_admin/external_feed and toggle External Feed on. This interface updates the three configuration columns mentioned above (see the feature tests in spec/requests/site_admin/external_feeds_spec.rb for validation details).
2. Select and Configure a Provider
Set the provider name in your website configuration:
website.external_feed_provider = "resales_online"
website.external_feed_config = {
api_key: "your_api_key",
api_id_sales: "your_sales_id"
}
The provider validates the presence of required keys via the required_config_keys method before executing requests.
3. Update Application Code
Replace all instances of the deprecated connector:
Old pattern:
MlsConnector.new(website).search(params)
New pattern:
Pwb::ExternalFeed::Manager.new(website).search(params)
Remove references to RETS-specific parameter names like p1 or p2. The manager automatically normalizes generic parameters (property_types, features, price, etc.) in its normalize_search_params method (lines 92-126 of manager.rb).
4. Verify Connectivity
The manager calls provider.available? during initialization to perform a lightweight health check. If the provider is unreachable, enabled? returns false and the site gracefully falls back to internal listings, preventing hard outages.
Practical Code Examples
Initializing the Manager in Controllers
# app/controllers/pwb/listings_controller.rb
def index
feed_manager = Pwb::ExternalFeed::Manager.new(current_website)
if feed_manager.enabled?
@search = feed_manager.search(search_params)
else
@search = Pwb::InternalListing.search(search_params)
end
end
The manager reads website.external_feed_config and instantiates a CacheStore during initialization (lines 10-15 of manager.rb).
Searching with Normalized Parameters
params = {
location: "Marbella",
property_types: ["apartment"],
min_price: 200_000,
max_price: 500_000,
sort: :price_desc,
page: 2,
per_page: 12,
features: ["pool", "garage"]
}
result = feed_manager.search(params)
The manager converts friendly keys into provider-specific codes before handing the hash to the provider's search method.
Retrieving a Single Property
property = feed_manager.find("REF12345", listing_type: :sale, locale: :en)
The ResalesOnline provider builds a detail URL, fetches JSON, and returns a NormalizedProperty instance (lines 58-84 of resales_online.rb).
Accessing Filter Options
options = feed_manager.filter_options(listing_type: :sale, locale: :es)
# => {
# locations: [...],
# property_types: [...],
# features: [...],
# ... other UI settings from SearchConfig
# }
The filter_options method merges cached provider data with site-wide SearchConfig values (lines 150-176 of manager.rb).
Adding a Custom Provider
- Create
app/services/pwb/external_feed/providers/my_feed.rbinheriting fromBaseProvider - Implement required methods (
search,find, etc.) - Register in
app/services/pwb/external_feed/registry.rb:
def self.register
register_provider(:my_feed, MyFeed)
end
No other code changes are required—the manager discovers the provider automatically via the registry.
Summary
- The legacy RETS/IDX connector was removed from
app/services/pwb/mls_connector.rbin December 2024 - The new system uses
ExternalFeed::Manageras a central façade for all third-party integrations - Configuration is controlled via three JSON columns on the websites table:
external_feed_enabled,external_feed_provider, andexternal_feed_config - The Provider Registry pattern allows adding new feeds (Airbnb, Zillow, etc.) without modifying core application code
- A
CacheStorewrapper around Rails cache reduces API traffic and improves response times per website
Frequently Asked Questions
What happened to the old MlsConnector class?
The MlsConnector class was deprecated and removed in December 2024 as noted in lines 4-7 of app/services/pwb/mls_connector.rb. All functionality has been superseded by the ExternalFeed::Manager system, which provides a cleaner abstraction over modern REST APIs rather than legacy MLS protocols.
How do I add a provider other than Resales Online?
Create a new class in app/services/pwb/external_feed/providers/ inheriting from BaseProvider and implement the required interface methods (search, find, similar, locations, property_types, available?). Register the provider in app/services/pwb/external_feed/registry.rb using register_provider(:your_symbol, YourClass). The manager will automatically detect and use it based on the external_feed_provider column value.
What happens if the external feed API is unavailable?
The manager calls provider.available? during initialization to check connectivity. If the provider returns false or raises an error, the manager's enabled? method returns false, and the application falls back to internal listings. This graceful degradation prevents your site from crashing when third-party services experience outages.
Where are external feed credentials stored?
Provider-specific credentials (API keys, IDs, etc.) are stored in the external_feed_config JSON column on the pwb_websites table, as implemented in app/models/pwb/website.rb. The ExternalFeed::Manager validates these credentials against the provider's required_config_keys before executing any requests, ensuring missing configuration is caught early.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →