# How to Migrate from Deprecated RETS/IDX to External Feeds in PropertyWebBuilder

> Learn how to migrate from deprecated RETSIDX to external feeds in PropertyWebBuilder. Discover the new ExternalFeed::Manager system for seamless third-party API integration.

- Repository: [Ed Tee/property_web_builder](https://github.com/etewiah/property_web_builder)
- Tags: migration-guide
- Published: 2026-03-01

---

**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`](https://github.com/etewiah/property_web_builder/blob/main/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`](https://github.com/etewiah/property_web_builder/blob/main/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`](https://github.com/etewiah/property_web_builder/blob/main/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`](https://github.com/etewiah/property_web_builder/blob/main/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`](https://github.com/etewiah/property_web_builder/blob/main/app/models/pwb/website.rb):

- `external_feed_enabled` (boolean) – toggles the feature on/off
- `external_feed_provider` (string) – stores the provider symbol (e.g., "resales_online")
- `external_feed_config` (hash) – contains provider-specific secrets like `api_key` and `api_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`](https://github.com/etewiah/property_web_builder/blob/main/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:

```ruby
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:**

```ruby
MlsConnector.new(website).search(params)

```

**New pattern:**

```ruby
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`](https://github.com/etewiah/property_web_builder/blob/main/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

```ruby

# 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`](https://github.com/etewiah/property_web_builder/blob/main/manager.rb)).

### Searching with Normalized Parameters

```ruby
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

```ruby
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`](https://github.com/etewiah/property_web_builder/blob/main/resales_online.rb)).

### Accessing Filter Options

```ruby
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`](https://github.com/etewiah/property_web_builder/blob/main/manager.rb)).

### Adding a Custom Provider

1. Create [`app/services/pwb/external_feed/providers/my_feed.rb`](https://github.com/etewiah/property_web_builder/blob/main/app/services/pwb/external_feed/providers/my_feed.rb) inheriting from `BaseProvider`
2. Implement required methods (`search`, `find`, etc.)
3. Register in [`app/services/pwb/external_feed/registry.rb`](https://github.com/etewiah/property_web_builder/blob/main/app/services/pwb/external_feed/registry.rb):

```ruby
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.rb`](https://github.com/etewiah/property_web_builder/blob/main/app/services/pwb/mls_connector.rb) in December 2024
- The new system uses `ExternalFeed::Manager` as 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`, and `external_feed_config`
- The Provider Registry pattern allows adding new feeds (Airbnb, Zillow, etc.) without modifying core application code
- A `CacheStore` wrapper 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`](https://github.com/etewiah/property_web_builder/blob/main/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`](https://github.com/etewiah/property_web_builder/blob/main/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`](https://github.com/etewiah/property_web_builder/blob/main/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.