FMP
May 13, 2025 10:07 PM - Sanzhi Kobzhan(Last modified: May 14, 2025 2:38 AM)
Image credit: financial news
import { fetch } from 'wix-fetch';
import wixData from 'wix-data';
import { getSecret } from 'wix-secrets-backend';
// Function to create a URL-friendly slug from the article title
function generateSlug(title) {
const safeTitle = title || "untitled";
const stopWords = /\b(the|a|an|and|or|but|in|on|at|to|for|of|with|by)\b/gi;
return safeTitle
.toLowerCase()
.slice(0, 60)
.replace(stopWords, '')
.replace(/[^\w\s-]/g, '')
.replace(/\s+/g, '-')
.replace(/-+/g, '-')
.trim();
}
// Main function to fetch and store news
export async function getStockNews() {
try {
// Get the API key from Secrets Manager
const apiKey = await getSecret('FMP_API_KEY');
if (!apiKey) throw new Error("Missing API key");
// FMP API endpoint
const url = `https://financialmodelingprep.com/api/v3/fmp/articles?page=0&size=30&apikey=${apiKey}`;
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000); // 10-second timeout
const response = await fetch(url, { method: "GET", signal: controller.signal });
clearTimeout(timeoutId);
if (!response.ok) throw new Error(`API request failed: ${response.status}`);
const news = await response.json();
if (!Array.isArray(news)) throw new Error("News is not an array");
// Remove duplicates by title
const seenTitles = new Set();
const uniqueNews = news.filter(item => {
if (!item.title || seenTitles.has(item.title)) return false;
seenTitles.add(item.title);
return true;
});
// Save each news item to the database
for (const item of uniqueNews) {
let slug = generateSlug(item.title);
if (!slug) continue;
let slugSuffix = 1;
let uniqueSlug = slug;
while (true) {
const existingItem = await wixData.query('NewsItems')
.eq('slug', uniqueSlug)
.find();
if (existingItem.items.length === 0) break;
uniqueSlug = `${slug}-${slugSuffix}`;
slugSuffix++;
}
const newsItem = {
title1: item.title || "Untitled",
content: item.content ? `<p>${item.content.replace(/[\r\n]+/g, '</p><p>')}</p>` : "No content available
",
image: item.image || "",
slug: uniqueSlug,
createdAt: item.date ? new Date(item.date) : new Date(),
updatedAt: new Date(),
};
const existingItem = await wixData.query('NewsItems')
.eq('title1', newsItem.title1)
.find();
if (existingItem.items.length > 0) {
await wixData.update('NewsItems', { ...newsItem, _id: existingItem.items[0]._id });
} else {
await wixData.insert('NewsItems', newsItem);
}
}
// Fetch the latest news items from the database
const result = await wixData.query('NewsItems')
.descending('createdAt')
.limit(30)
.find();
const uniqueResults = [];
const seenDbTitles = new Set();
for (const item of result.items) {
if (!seenDbTitles.has(item.title1)) {
uniqueResults.push(item);
seenDbTitles.add(item.title1);
}
}
return uniqueResults;
} catch (err) {
try {
const dbItems = await wixData.query('NewsItems')
.descending('createdAt')
.limit(30)
.find();
const uniqueDbItems = [];
const seenDbTitles = new Set();
for (const item of dbItems.items) {
if (!seenDbTitles.has(item.title1)) {
uniqueDbItems.push(item);
seenDbTitles.add(item.title1);
}
}
return uniqueDbItems;
} catch (dbErr) {
throw err;
}
}
}
// Function to fetch a single article by slug
export async function getNewsBySlug(slug) {
try {
const result = await wixData.query('NewsItems')
.eq('slug', slug)
.find();
return result.items.length > 0 ? result.items[0] : null;
} catch (err) {
throw err;
}
}
import { getStockNews } from 'backend/news.jsw';
import wixLocation from 'wix-location';
import wixStorage from 'wix-storage';
//formatting
function stripHtmlTags(html) {
return html.replace(/<[^>]*>/g, '').replace(/\s+/g, ' ').trim();
}
function generateSlug(title) {
const safeTitle = title || "untitled";
const stopWords = /\b(the|a|an|and|or|but|in|on|at|to|for|of|with|by)\b/gi;
return safeTitle
.toLowerCase()
.slice(0, 60)
.replace(stopWords, '')
.replace(/[^\w\s-]/g, '')
.replace(/\s+/g, '-')
.replace(/-+/g, '-')
.trim();
}
//Getting Stock News
$w.onReady(async function () {
try {
const news = await getStockNews();
if (!Array.isArray(news) || news.length === 0) {
$w("#repeater1").data = [];
$w("#repeater1").hide();
if ($w("#errorMessage").length) {
$w("#errorMessage").text = "No news available at this time.";
$w("#errorMessage").show();
}
return;
}
const seenTitles = new Set();
const uniqueNews = news.filter(item => {
if (seenTitles.has(item.title1)) return false;
seenTitles.add(item.title1);
return true;
});
const first9 = uniqueNews.slice(0, 10).map((item, index) => {
const slug = item.slug || generateSlug(item.title1);
return {
...item,
_id: item._id ? item._id.toString() : index.toString(),
slug: slug,
};
});
//Populating data
$w("#repeater1").data = first9;
$w("#repeater1").show();
if ($w("#errorMessage").length) {
$w("#errorMessage").hide();
}
$w("#repeater1").onItemReady(($item, itemData) => {
if (itemData.image) {
$item("#image1").src = itemData.image;
$item("#image1").expand();
} else {
$item("#image1").collapse();
}
$item("#textTitle").text = itemData.title1 || "No Title";
$item("#textSnippet").text = itemData.content
? stripHtmlTags(itemData.content).slice(0, 120) + "..."
: "No snippet available.";
$item("#linkButton").label = "Read More";
$item("#linkButton").onClick(() => {
if (!itemData.slug) {
$item("#textTitle").text = "Error: Missing slug.";
return;
}
try {
wixStorage.session.setItem("selectedNews", JSON.stringify(itemData));
wixLocation.to(`/full-news?slug=${itemData.slug}`);
} catch (navError) {
$item("#textTitle").text = "Error: Navigation failed.";
}
});
});
} catch (error) {
$w("#repeater1").data = [];
$w("#repeater1").hide();
if ($w("#errorMessage").length) {
$w("#errorMessage").text = "Failed to load news.";
$w("#errorMessage").show();
}
}
});


Explanation of the Full News Code
Replace PUT_YOUR_WEBSITE_ADRESS with your website and YOUR APP NAME with the name of your app/website
wixLocation.to(`/full-news/${itemData.slug}`);
slugFromUrl = wixLocation.path[1];
const articleUrl = `https://PUT_YOUR_WEBSITE_ADRESS/full-news/${slugFromUrl}`;
Technical analysis is a fundamental approach used by traders to forecast price movements based on historical market data...
Introduction In the competitive landscape of modern business, companies that consistently outperform their peers ofte...
Introduction Apple (NASDAQ: AAPL) has been working to diversify its supply chain, reducing dependence on China due to...