Skip to content

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 load
loadFeed(`${FEED_BASE}?limit=10`);
function escapeHtml(str) {
return str
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}

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';

Reusable Client Module

For larger projects, encapsulate the Feed API calls in a module:

feed-client.js
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 page
const { items } = await getFeed({ types: 'news', limit: 10 });
// Detail page
const item = await getItem('550e8400-e29b-41d4-a716-446655440000');
// Build-time index
const 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:

scripts/build-index.mjs
import { getAllItems } from './feed-client.js';
import { writeFileSync } from 'fs';
const items = await getAllItems();
// Write a static JSON index for client-side search
writeFileSync('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 minutes
setInterval(pollFeed, 2 * 60 * 1000);
pollFeed(); // initial load

ICS 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';