JavaScript Integration
FlareBuilder’s Feed API is a plain HTTP JSON endpoint with full CORS support. You can call it from any JavaScript environment — a browser, a Node.js script, a Cloudflare Worker, a Deno server, or anywhere else that can make HTTP requests. No SDK, no build step, no framework required.
The Basics
const res = await fetch('https://your-org.flarebuilder.com/feed');const feed = await res.json();
for (const item of feed.items) { console.log(item.title, item.date_published);}That’s it. Every example on this page builds on this.
Displaying a List in the Browser
Given an HTML page with a <ul id="feed-list"> and an optional <button id="load-more">:
<ul id="feed-list"></ul><button id="load-more" hidden>Load more</button>const FEED_BASE = 'https://your-org.flarebuilder.com/feed';const list = document.getElementById('feed-list');const loadMoreBtn = document.getElementById('load-more');
let nextUrl = null;
async function loadFeed(url) { const res = await fetch(url); const data = await res.json();
for (const item of data.items) { const section = item.sections.find(s => s.id === 'content')?.data ?? {};
const li = document.createElement('li'); li.innerHTML = ` <article> <h2><a href="/item.html?id=${item.id}">${escapeHtml(item.title)}</a></h2> ${section.summary ? `<p>${escapeHtml(section.summary)}</p>` : ''} <time datetime="${item.date_published}"> ${new Date(item.date_published).toLocaleDateString()} </time> </article> `; list.appendChild(li); }
nextUrl = data.pagination.has_more ? data.pagination.next : null; loadMoreBtn.hidden = !nextUrl;}
loadMoreBtn.addEventListener('click', () => loadFeed(nextUrl));
// Initial loadloadFeed(`${FEED_BASE}?limit=10`);
function escapeHtml(str) { return str .replace(/&/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"');}Displaying a Single Item
Fetch a single item by its UUID using the /p/:id endpoint:
const id = new URLSearchParams(location.search).get('id');const res = await fetch(`https://your-org.flarebuilder.com/p/${id}`);
if (!res.ok) { document.getElementById('content').textContent = 'Not found.';} else { const item = await res.json(); const content = item.sections.find(s => s.id === 'content')?.data ?? {};
document.getElementById('title').textContent = item.title; document.getElementById('body').innerHTML = content.body ?? ''; // trusted HTML from editor document.getElementById('published').textContent = new Date(item.date_published).toLocaleDateString();}Fetch All Pages
To retrieve every item (for example, to build a search index or generate a static site):
async function fetchAll(baseUrl, params = {}) { const items = []; const qs = new URLSearchParams({ limit: 100, ...params }); let url = `${baseUrl}?${qs}`;
while (url) { const res = await fetch(url); const data = await res.json(); items.push(...data.items); url = data.pagination.has_more ? data.pagination.next : null; }
return items;}
const allItems = await fetchAll('https://your-org.flarebuilder.com/feed', { types: 'event', status: 'active',});console.log(`Fetched ${allItems.length} events`);Filtering
Pass any Feed API parameters as query string values:
const url = 'https://your-org.flarebuilder.com/feed?templates=event&limit=20';// OR: news or announcementsconst url = 'https://your-org.flarebuilder.com/feed?tags=news,announcements';
// AND: must have both tagsconst url = 'https://your-org.flarebuilder.com/feed?tags=[news,featured]';const start = '2025-06-01T00:00:00';const end = '2025-06-30T23:59:59';const url = `https://your-org.flarebuilder.com/feed?event_start=${start}&event_end=${end}&timezone=America/New_York`;Reusable Client Module
For larger projects, encapsulate the Feed API calls in a module:
const BASE = 'https://your-org.flarebuilder.com';
/** * Fetch a page of feed items. * @param {Object} params - Query parameters (types, tags, limit, cursor, etc.) * @returns {Promise<{items: object[], next: string|null, hasMore: boolean}>} */export async function getFeed(params = {}) { const qs = new URLSearchParams(); for (const [k, v] of Object.entries(params)) { if (v !== undefined && v !== null) qs.set(k, String(v)); } const res = await fetch(`${BASE}/feed?${qs}`); if (!res.ok) throw new Error(`Feed API ${res.status}`); const data = await res.json(); return { items: data.items, next: data.pagination.next, hasMore: data.pagination.has_more, };}
/** * Fetch a single content item by ID. * @param {string} id - Content item UUID * @returns {Promise<object>} */export async function getItem(id) { const res = await fetch(`${BASE}/p/${encodeURIComponent(id)}`); if (res.status === 404) return null; if (!res.ok) throw new Error(`Feed API ${res.status}`); return res.json();}
/** * Fetch all pages of results. * @param {Object} params - Same as getFeed, minus cursor * @returns {Promise<object[]>} */export async function getAllItems(params = {}) { const items = []; let cursor = null; do { const page = await getFeed({ ...params, limit: 100, cursor }); items.push(...page.items); cursor = page.hasMore ? new URL(page.next).searchParams.get('cursor') : null; } while (cursor); return items;}Usage:
import { getFeed, getItem, getAllItems } from './feed-client.js';
// List pageconst { items } = await getFeed({ types: 'news', limit: 10 });
// Detail pageconst item = await getItem('550e8400-e29b-41d4-a716-446655440000');
// Build-time indexconst all = await getAllItems({ types: 'event' });Node.js / Server-Side
The same code works in Node.js 18+ (which ships fetch natively) and in any runtime that supports the Fetch API:
import { getAllItems } from './feed-client.js';import { writeFileSync } from 'fs';
const items = await getAllItems();
// Write a static JSON index for client-side searchwriteFileSync('public/search-index.json', JSON.stringify( items.map(({ id, title, sections }) => ({ id, title, summary: sections[0]?.data?.summary ?? '', }))));
console.log(`Indexed ${items.length} items`);For older Node.js versions (< 18) or environments without fetch, use any HTTP library (node-fetch, axios, undici) — the API surface is identical.
Cloudflare Workers
The Feed API runs on the same Cloudflare network as Workers, so requests from a Worker are extremely fast:
export default { async fetch(request, env) { const feedRes = await fetch( 'https://your-org.flarebuilder.com/feed?templates=news&limit=5' ); const feed = await feedRes.json();
const html = `<!doctype html><html><body> <h1>${feed.title}</h1> <ul> ${feed.items.map(item => `<li>${item.title}</li>`).join('')} </ul> </body></html>`;
return new Response(html, { headers: { 'Content-Type': 'text/html' }, }); },};Efficient Polling with ETags
If you poll the feed on a timer, use ETags to avoid processing unchanged responses:
let etag = null;
async function pollFeed() { const headers = etag ? { 'If-None-Match': etag } : {}; const res = await fetch('https://your-org.flarebuilder.com/feed', { headers });
if (res.status === 304) { return; // Nothing changed }
etag = res.headers.get('etag'); const data = await res.json(); renderFeed(data.items);}
// Poll every 2 minutessetInterval(pollFeed, 2 * 60 * 1000);pollFeed(); // initial loadICS Calendar Subscription
Link directly to the ICS feed URL — the browser or OS will offer to subscribe:
function addCalendarLink(containerId, type = 'event') { const url = `https://your-org.flarebuilder.com/feed?format=ics&templates=${type}`; const a = document.createElement('a'); a.href = url; a.download = 'calendar.ics'; a.textContent = 'Subscribe to calendar'; document.getElementById(containerId).appendChild(a);}Or open it programmatically (e.g. after a button click):
window.location.href = 'https://your-org.flarebuilder.com/feed?format=ics&templates=event';