[object Object]

← back to Stars of Design

API: add /api/designers/random + /api/firms/random + /api/clients/random (parity with asim)

ba5954a93869a472bcc89009e7e966da1dcfbadc · 2026-05-13 09:29:17 -0700 · Steve Abrams

Files touched

Diff

commit ba5954a93869a472bcc89009e7e966da1dcfbadc
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed May 13 09:29:17 2026 -0700

    API: add /api/designers/random + /api/firms/random + /api/clients/random (parity with asim)
---
 routes/public.js | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/routes/public.js b/routes/public.js
index 8d93fb1..0655c4f 100644
--- a/routes/public.js
+++ b/routes/public.js
@@ -401,6 +401,9 @@ router.get('/api', (_req, res) => {
       'GET /api/clients/:id/comments':   'Comments on a DW client profile (chronological)',
       'POST /api/clients/:id/like':      'Toggle like on a DW client (anonymous ID via cookie)',
       'POST /api/clients/:id/comment':   'Post a comment on a DW client (name + body, optional email)',
+      'GET /api/designers/random':       'Random designer — JSON {slug, display_name}',
+      'GET /api/firms/random':           'Random firm — JSON {slug, name}',
+      'GET /api/clients/random':         'Random DW client — JSON {slug, name}',
     },
     notes: [
       'Profiles are city + state only; no street addresses, no phone numbers (PII policy).',
@@ -457,6 +460,40 @@ router.get('/api/stats', async (_req, res, next) => {
   } catch (e) { next(e); }
 });
 
+// One-shot random discovery endpoints. Used by /random/* redirect helpers
+// (parity with asim) and by humans hitting "feeling lucky"-style UX.
+// 30s/60s SWR — cache lifespan is short by design so the random feel
+// stays fresh while keeping pg hits cheap under repeat polling.
+router.get('/api/designers/random', (_req, res, next) => {
+  try {
+    const all = data.load();
+    if (!all.length) return res.status(503).json({ error: 'no-data' });
+    const pick = all[Math.floor(Math.random() * all.length)];
+    res.setHeader('Cache-Control', 'public, max-age=30, stale-while-revalidate=60');
+    res.json({ slug: pick.slug, display_name: pick.name || pick.full_name || null });
+  } catch (e) { next(e); }
+});
+
+router.get('/api/firms/random', async (_req, res, next) => {
+  try {
+    const rows = await firmsLib.listFirms({ limit: 1000 });
+    if (!rows.length) return res.status(503).json({ error: 'no-data' });
+    const pick = rows[Math.floor(Math.random() * rows.length)];
+    res.setHeader('Cache-Control', 'public, max-age=30, stale-while-revalidate=60');
+    res.json({ slug: pick.slug, name: pick.name || null });
+  } catch (e) { next(e); }
+});
+
+router.get('/api/clients/random', async (_req, res, next) => {
+  try {
+    const rows = await clientsLib.listClients({ limit: 1000 });
+    if (!rows.length) return res.status(503).json({ error: 'no-data' });
+    const pick = rows[Math.floor(Math.random() * rows.length)];
+    res.setHeader('Cache-Control', 'public, max-age=30, stale-while-revalidate=60');
+    res.json({ slug: pick.slug, name: pick.full_name || pick.name || null });
+  } catch (e) { next(e); }
+});
+
 router.post('/api/clients/:id/like', async (req, res, next) => {
   try {
     const id = parseInt(req.params.id, 10);

← d54a257 sitemap.xml: emit Last-Modified header for conditional GET (  ·  back to Stars of Design  ·  API: add /random/designer + /random/firm + /random/client re da27142 →