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}`;
Sep 10, 2024 7:33 AM - Parth Sanghvi
When it comes to downloading historical stock data, Yahoo Finance has been a popular choice for many. However, for those looking for more comprehensive, accurate, and flexible options, Financial Modeling Prep (FMP) offers a suite of powerful tools and services that stand out as superior alternatives...
Nov 8, 2024 5:30 AM - Sanzhi Kobzhan
When it comes to investing in stocks, one of the key decisions an investor must make involves determining the intrinsic value of a company's shares. Equity valuation isn't just about numbers, it's an art mixed with science, aiming to predict future performance based on current and historical data. H...
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...