FMP
May 13, 2025(Last modified: May 14, 2025)



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}`;
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...
MicroStrategy Incorporated (NASDAQ:MSTR) is a prominent business intelligence company known for its software solutions a...