Real Time News Aggregator with NodeJS and ExpressJS
In this article, we will create a real time news application with the help of NodeJS and ExpressJS. This article consists of several main functionalities. First, we will display the news article. Then we have implemented the search functionality to search news based on the title of the news. Then we get the feature to have sorting by date and we can get news of a specific date.
Output Preview: Let us have a look at how the final output will look like.
Pre-requisites:
Approach to create a Real Time News Aggregator:
We will make use of NodeJS, ExpressJS, EJS and Tailwind CSS to develop our Real Time News Aggregator Application. In this project we will implement two important features, one is displaying the news article and then we can further explore more about each particular news by clicking on Read more.
Project Structure:
Steps to create the Project:
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: The below mentioned code 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=${searchTerm}&apiKey=679b913ddb014617bcc93a0bb89ee1ee`
);
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=${date}&to=${date}&sortBy=popularity&apiKey=679b913ddb014617bcc93a0bb89ee1ee`
);
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:
Contact Us