← 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 →