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:

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 --yesStep 2: Install Dependencies
npm install express axios ejs tailwindcss nodemon pathStep 3: Start the server
nodemon server.jsUpdated 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.
<!-- ../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>
// 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:
.gif)