Real Time News Aggregator with NodeJS and ExpressJS

Last Updated : 31 Mar, 2026

A Real-Time News Aggregator using Node.js and Express.js fetches and displays updated news articles, with features like search by title, sorting by date, and filtering news for specific dates.

Prerequisites

Ensure the required runtime and dependencies are installed before starting the project.

Approach

Design a real-time news aggregator using a full-stack Node.js setup with dynamic rendering and UI styling.

  • Use Node.js and Express.js for backend server and routing.
  • Use EJS for dynamic templating and rendering news data.
  • Use Tailwind CSS for responsive and modern UI design.
  • Fetch and display news articles in real-time.
  • Implement “Read More” functionality for detailed news view.

Project Structure:

Screenshot-2024-03-16-100838
Project Folder Structure

Steps to create Real Time News Aggregator

Follow these steps to build a dynamic news application using Node.js and Express.js.

Step 1: Initialize the Project

npm init --yes

Step 2: Install Dependencies

npm install express axios ejs tailwindcss nodemon path

Step 3: Start the server

nodemon server.js

Updated Dependencies:

"dependencies": {
    "axios": "^1.6.8",
    "cors": "^2.8.5",
    "ejs": "^3.1.9",
    "express": "^4.18.3",
    "nodemon": "^3.1.0",
    "path": "^0.12.7",
    "tailwindcss": "^3.4.1"
  }

Example: Code to implements Real Time News Aggregator with NodeJS and ExpressJS.

HTML
<!-- ../Views/index.ejs !-->

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>News Aggregator</title>
    <link
      href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css"
      rel="stylesheet"
    />
  </head>
  <body class="bg-gray-100">
    <div
      class="container mx-auto mt-8 flex flex-col items-center justify-center"
    >
      <form
        action="/search"
        method="GET"
        class="flex items-center justify-center mt-4"
      >
        <input
          class="w-64 px-4 py-2 rounded-l-lg border border-gray-300 focus:outline-none focus:border-blue-500"
          name="search"
          type="search"
          placeholder="Search"
          aria-label="Search"
        />
        <button
          class="px-4 py-2 bg-red-500 text-white rounded-r-lg hover:bg-red-600"
          type="submit"
        >
          Search
        </button>
      </form>

      <form action="/sort-by-date" method="GET">
        <button
          class="mt-4 px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600"
          type="submit"
        >
          Sort by Date
        </button>
      </form>

      <form action="/news-by-date" method="GET" class="mt-4">
        <label for="specific-date" class="block mb-2"
          >Get News for Specific Date:</label
        >
        <input
          id="specific-date"
          class="w-64 px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:border-blue-500"
          name="date"
          type="date"
          aria-label="Specific Date"
        />
        <button
          class="px-4 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600"
          type="submit"
        >
          Get News
        </button>
      </form>

      <h1 class="text-3xl font-bold my-4 text-center">Latest News</h1>
      <div
        id="news-container"
        class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4"
      >
        <% news.forEach(article => { %>
        <div class="article border p-4 bg-white rounded-lg shadow-md">
          <h2 class="text-lg font-bold mb-2"><%= article.title %></h2>
          <p class="text-gray-700"><%= article.description %></p>
          <p class="text-gray-700 article-date">
            Published at: <%= article.publishedAt %>
          </p>
          <a
            href="<%= article.url %>"
            class="text-blue-600 font-semibold mt-2 inline-block"
            >Read more</a
          >
        </div>
        <% }); %>
      </div>
    </div>
  </body>
</html>
JavaScript
// server.js

const express = require("express");
const path = require("path");
const axios = require("axios");

const app = express();

app.set("views", path.join(__dirname, "views"));
app.set("view engine", "ejs");

app.get("/", async (req, res) => {
  try {
    const response = await axios.get(
      "https://newsapi.org/v2/top-headlines?country=in&apiKey=679b913ddb014617bcc93a0bb89ee1ee"
    );
    const data = response.data;

    res.render("index", { news: data.articles });
  } catch (error) {
    console.error("Error fetching news:", error);
    res.status(500).send("Error fetching news. Please try again later.");
  }
});

app.get("/search", async (req, res) => {
  try {
    const searchTerm = req.query.search;
    const response = await axios.get(
      `https://newsapi.org/v2/everything?q=$%7BsearchTerm%7D&apiKey=679b913ddb014617bcc93a0bb89ee1ee%60
    );
    const data = response.data.articles;

    const news = data.filter((dataItem) => dataItem.title?.toLowerCase().includes(searchTerm?.toLowerCase()));

    res.render("index", { news });
  } catch (error) {
    console.error("Error fetching search results:", error);
    res
      .status(500)
      .send("Error fetching search results. Please try again later.");
  }
});

app.get("/sort-by-date", async (req, res) => {
  try {
    const response = await axios.get(
      "https://newsapi.org/v2/top-headlines?country=in&apiKey=679b913ddb014617bcc93a0bb89ee1ee"
    );
    const data = response.data.articles;

    data.sort((a, b) => new Date(b.publishedAt) - new Date(a.publishedAt));

    res.render("index", { news: data });
  } catch (error) {
    console.error("Error sorting articles by date:", error);
    res.status(500).send("Error sorting articles by date. Please try again later.");
  }
});

app.get("/news-by-date", async (req, res) => {
  try {
    const date = req.query.date;
    const response = await axios.get(
      `https://newsapi.org/v2/everything?q=*&from=$%7Bdate%7D&to=$%7Bdate%7D&sortBy=popularity&apiKey=679b913ddb014617bcc93a0bb89ee1ee%60
    );
    const data = response.data.articles;

    res.render("index", { news: data });
  } catch (error) {
    console.error("Error fetching news by date:", error);
    res.status(500).send("Error fetching news by date. Please try again later.");
  }
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});

Output:

ezgifcom-animated-gif-maker-(5)

Comment

Explore