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}`;
Nov 22, 2024 5:08 AM - Parth Sanghvi
Fundamental analysis is one of the most essential tools for investors and analysts alike, helping them assess the intrinsic value of a stock, company, or even an entire market. It focuses on the financial health and economic position of a company, often using key data such as earnings, expenses, ass...
Dec 17, 2024 8:58 AM - Sanzhi Kobzhan
Tesla, one of the world’s most talked-about electric vehicle manufacturers, attracts a lot of attention from investors and market watchers. By examining a snapshot of Tesla’s financial ratios—such as those provided by FinancialModelingPrep’s Ratios API—we can get a clearer picture of the company’s f...
Dec 22, 2024 7:59 AM - Sanzhi Kobzhan
When it comes to cutting-edge software and data analytics, Palantir Technologies (NYSE: PLTR) is often front and center. But for many investors, it’s important to consider alternative or complementary stocks in the same sector that may offer robust growth potential. As PLTR looks expensive (overvalu...