[object Object]

← back to Commercialrealestate

graceful-degrade /api/leads/frank + /api/broker (502 -> 200 when cre DB absent)

cb778e729ec546dd94067d09babb8fcbfc883e59 · 2026-07-16 17:54:46 -0700 · Steve

On prod (Kamatera) the broker DB module loads but there's no 'cre' Postgres,
so pool.query throws and both endpoints hard-502'd -> red console errors on
every internal page load. Return the front-end-safe shapes at 200 instead:
leads:[]+unavailable (panel already shows 'no leads (broker DB unavailable)'),
and the same {error} body (modal handles d.error; featured card guards !d.broker).
Verified by reproducing the prod condition (brokerdb loaded, DB unreachable):
both now 200; working-DB success path untouched.

Files touched

Diff

commit cb778e729ec546dd94067d09babb8fcbfc883e59
Author: Steve <steve@designerwallcoverings.com>
Date:   Thu Jul 16 17:54:46 2026 -0700

    graceful-degrade /api/leads/frank + /api/broker (502 -> 200 when cre DB absent)
    
    On prod (Kamatera) the broker DB module loads but there's no 'cre' Postgres,
    so pool.query throws and both endpoints hard-502'd -> red console errors on
    every internal page load. Return the front-end-safe shapes at 200 instead:
    leads:[]+unavailable (panel already shows 'no leads (broker DB unavailable)'),
    and the same {error} body (modal handles d.error; featured card guards !d.broker).
    Verified by reproducing the prod condition (brokerdb loaded, DB unreachable):
    both now 200; working-DB success path untouched.
---
 scripts/serve.js | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/scripts/serve.js b/scripts/serve.js
index ee3646b..bee3d33 100644
--- a/scripts/serve.js
+++ b/scripts/serve.js
@@ -320,7 +320,11 @@ app.get('/api/leads/frank', async (req, res) => {
         FROM ll GROUP BY broker_id, name, firm, agent_type
         ORDER BY fit_score DESC, total_value DESC LIMIT $1`, [limit])).rows;
     res.json({ leads, scoredBy: 'Arcstone-financeability of current listings (mixed-use/condo×3, multifamily/commercial×2). Business data; no owner names.' });
-  } catch (e) { res.status(502).json({ error: String(e.message).split('\n')[0], leads: [] }); }
+  } catch (e) {
+    // Graceful degrade (prod has no `cre` Postgres → pool.query throws): the front-end iterates
+    // d.leads, so an empty-array 200 renders "no leads" cleanly instead of a 502 console error.
+    res.json({ leads: [], unavailable: true, reason: String(e.message).split('\n')[0] });
+  }
 });
 
 // Licensed agents/brokers — every RE agent CRCP harvested, backed by a REAL California DRE
@@ -708,7 +712,12 @@ app.get('/api/broker', async (req, res) => {
       compsNote: comps.length ? 'Recent sold comps in this broker’s areas (Redfin) with sold dates + MLS# — area context, not this broker’s own closings.' : null,
       expiredNote: expired.length ? 'Listings of this broker that now appear in the sold feed (best-effort address match).' : 'No off-market/expired feed yet — none of this broker’s listings matched a sold record.',
       closedNote: closed.length ? null : 'Per-broker closed history needs a gated detail-scrape; area sales shown instead.' });
-  } catch (e) { res.status(502).json({ error: String(e.message).split('\n')[0] }); }
+  } catch (e) {
+    // Graceful degrade (prod has no `cre` Postgres → pool.query throws): SAME {error} body the
+    // front-end already handles (modal shows d.error; featured card guards !d.broker), but 200 not
+    // 502 so it isn't a red console error on every internal page load.
+    res.json({ error: String(e.message).split('\n')[0], unavailable: true });
+  }
 });
 
 // Firm detail — the roster (brokers/agents at the firm) + aggregate listings. Powers the click-through

← b4b3c10 add CRCP origin-vs-live drift canary (DTD verdict D)  ·  back to Commercialrealestate  ·  CRCP list view: sortable + fully hrefd rows, inline row expa 123b0e3 →