News Media Platform with MERN Stack
Last Updated :
07 Mar, 2024
In this article, we’ll walk through the step-by-step process of creating a News Media Platform using the MERN (MongoDB, ExpressJS, React, NodeJS) stack. This project will showcase how to set up a full-stack web application where users can view a news article, add a new news article, and delete one.
Preview of final output: Let us have a look at how the final application will look like.
Output
Prerequisites:
Approach to Create a News Media Platform:
- List all the requirement for the project and make the structure of the project accordingly.
- Chooses the required dependencies and requirement which are more suitable for the project.
For Backend:
- Create a directory named model inside root directory.
- Create a javascript file named articleSchema.js in the model directory for collection news schema.
- Then create another route directory inside root(Backend folder).
- Create another javascript file named articleRoute.js to handle API request.
For Frontend:
- Create a components directory inside root directory(Frontend folder).
- Create three file of javascript inside components folder namely DeleteArticle.jsx, NewsArticleForm.jsx and NewsList.jsx which are used to delete, add new article and show a list of news respectively.
Steps to Create the Backend:
Step 1: Create a directory for project
mkdir Backend
cd Backend
Step 2: Initialized the Express app and installing the required packages
npm init -y
npm i express mongoose cors nodemon
Project Structure:
Backend project structure
The package.json file of backend will look like:
"dependencies": {
"cors": "^2.8.5",
"express": "^4.18.2",
"mongoose": "^8.2.0",
"nodemon": "^3.1.0"
}
Example : Below is an example of creating a server for New Media platform.
Javascript
const express = require( 'express' );
const mongoose = require( 'mongoose' );
const bodyParser = require( 'body-parser' );
const articleRouter = require( './route/articleRoute.js' );
const cors = require( 'cors' )
const app = express();
const PORT = process.env.PORT || 5000;
app.use(cors())
app.use(bodyParser.json());
useNewUrlParser: true ,
useUnifiedTopology: true
})
.then(() => console.log( 'MongoDB connected' ))
. catch (err => console.log(err));
app.use( '/api/articles' , articleRouter);
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
|
Javascript
const mongoose = require( 'mongoose' );
const articleSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
author: {
type: String,
required: true
},
content: {
type: String,
required: true
},
category: {
type: String,
required: true
},
createdAt: {
type: Date,
default : Date.now
}
});
const Article = mongoose.model( 'Article' , articleSchema);
module.exports = Article;
|
Javascript
const express = require( 'express' );
const router = express.Router();
const Article = require( '../model/articleSchema.js' );
router.get( '/' , async (req, res) => {
try {
const articles = await Article.find();
res.json(articles);
} catch (err) {
res.status(500).json({
message: err.message
});
}
});
router.get( '/:id' , async (req, res) => {
try {
const article = await Article.findById(req.params.id);
if (article == null ) {
return res.status(404).json({
message: 'Article not found'
});
}
res.json(article)
} catch (err) {
return res.status(500).json({
message: err.message
});
}
});
router.post( '/' , async (req, res) => {
try {
const article = new Article({
title: req.body.title,
author: req.body.author,
content: req.body.content,
category: req.body.category
});
const newArticle = await article.save();
res.status(201).json(newArticle);
} catch (err) {
res.status(400).json({
message: err.message
});
}
});
router.put( '/:id' , async (req, res) => {
try {
const article = await Article.findByIdAndUpdate(
req.params.id, req.body, { new : true });
if (!article) {
return res.status(404).json({
message: 'Article not found'
});
}
res.json(article);
} catch (err) {
res.status(400).json({ message: err.message });
}
});
router. delete ( '/:id' , async (req, res) => {
try {
const article = await Article.findByIdAndDelete(req.params.id);
if (!article) {
return res.status(404).json({
message: 'Article not found'
});
}
res.json({ message: 'Article deleted' });
} catch (err) {
res.status(500).json({
message: err.message
});
}
});
module.exports = router;
|
Start your server using the following command.
node index.js
Steps to Create the Frontend:
Step 1: Initialized the React App with Vite and installing the required packages
npm create vite@latest -y
->Enter Project name: "Frontend"
->Select a framework: "React"
->Select a Variant: "Javascript"
cd Frontend
npm install
Project Structure:
Frontend Project structure
The package.json file of frontend will look like:
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.2.56",
"@types/react-dom": "^18.2.19",
"@vitejs/plugin-react": "^4.2.1",
"vite": "^5.1.4"
}
}
Example: Below is an example of creating a frontend of News Media platform.
CSS
*,
*::before,
*::after {
box-sizing: border-box;
padding : 0 ;
margin : 0 ;
}
body {
display : grid;
min-height : 100 vh;
background : #fff ;
background-image : linear-gradient( white , green );
}
h 1 {
text-align : center ;
margin-top : 20px ;
}
h 2 {
text-align : center ;
margin-top : 20px ;
}
.container {
display : flex;
flex-wrap: wrap;
justify- content : center ;
max-width : 1200px ;
margin-block: 2 rem;
gap: 2 rem;
}
.card {
display : flex;
flex- direction : column;
width : clamp( 20 rem, calc( 20 rem + 2 vw), 22 rem);
overflow : hidden ;
box-shadow: 0 . 1 rem 1 rem rgba( 0 , 0 , 0 , 0.1 );
border-radius: 1em ;
background : #ECE9E6 ;
background : linear-gradient(to right , #FFFFFF , #ECE9E6 );
}
.card__body {
padding : 1 rem;
display : flex;
flex- direction : column;
gap: . 5 rem;
}
.tag {
align-self: flex-start;
padding : . 25em . 75em ;
border-radius: 1em ;
font-size : . 75 rem;
}
.tag- green {
background : green ;
color : #fafafa ;
}
.card__body h 4 {
font-size : 1.5 rem;
text-transform : capitalize ;
}
.card__footer {
display : flex;
padding : 1 rem;
margin-top : auto ;
}
.user {
display : flex;
gap: . 2 rem;
}
.form-container {
width : 500px ;
margin : 50px auto ;
background-color : white ;
padding : 20px ;
border-radius: 20px ;
}
.form-group {
margin-bottom : 15px ;
}
.form-group label {
display : block ;
margin-bottom : 5px ;
}
.form-group input {
width : 100% ;
padding : 5px ;
}
.form-group textarea {
width : 100% ;
padding : 5px ;
}
.form-group select {
width : 100% ;
padding : 5px ;
background-color : #f2f2f2 ;
border : none ;
border-radius: 5px ;
appearance: none ;
-webkit-appearance: none ;
-moz-appearance: none ;
cursor : pointer ;
}
.form-group select::-ms-expand {
display : none ;
}
.form-group select:focus {
outline : none ;
box-shadow: 0 0 5px rgba( 0 , 0 , 0 , 0.2 );
}
.form-group button {
background-color : #4CAF50 ;
color : white ;
padding : 5px 10px ;
border : none ;
cursor : pointer ;
width : 100% ;
}
.form-group button:hover {
background-color : #45a049 ;
}
button {
background-color : #4CAF50 ;
color : white ;
padding : 10px 20px ;
border : none ;
border-radius: 5px ;
cursor : pointer ;
display : block ;
margin : 0 auto ;
}
button:hover {
background-color : #45a049 ;
}
.around {
width : 100% ;
display : flex;
justify- content : space-between;
}
.around h 1 {
margin-left : 50px ;
}
.around button {
align-self: flex-end;
}
#tbl button {
background-color : #cb2d3e !important ;
}
#tbl button:hover {
background-color : #c2424f ;
}
#tbl-head {
width : 80% ;
background-color : white ;
padding : 20px ;
margin : 50px auto ;
border-radius: 20px ;
}
#tbl {
width : 90% ;
border-collapse : collapse ;
margin : 0 auto ;
margin-bottom : 50px ;
margin-top : 30px ;
}
#tbl td,
#tbl th {
border : 1px solid #ddd ;
padding : 8px ;
}
#tbl th {
padding-top : 12px ;
padding-bottom : 12px ;
text-align : left ;
color : green ;
}
|
Javascript
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'
ReactDOM.createRoot(document.getElementById( 'root' )).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)
|
Javascript
import React, { useState } from "react" ;
import NewArticleForm from "./components/NewArticleForm.jsx" ;
import DeleteArticle from "./components/DeleteArticle.jsx" ;
import NewsList from "./components/NewsList.jsx" ;
import "./index.css" ;
function App() {
const [isAuther, setAuther] = useState( false );
return (
<div>
<div className= "around" >
<h1>GFG News App</h1>
<button onClick={() => setAuther(!isAuther)}>
Switch to {isAuther ? "Viewer" : "Author" }
</button>
</div>
{isAuther ? (
<>
<NewArticleForm setAuther={setAuther} />
<DeleteArticle />
</>
) : (
<NewsList />
)}
</div>
);
}
export default App;
|
Javascript
import { useEffect, useState } from "react" ;
const DeleteArticle = () => {
const [articles, setArticles] = useState([]);
const [deleted, setDelete] = useState( true );
useEffect(() => {
.then((response) => response.json())
.then((data) => setArticles(data))
. catch ((error) =>
console.error( "Error fetching articles:" , error));
}, [deleted]);
const handleDelete = async (articleid) => {
try {
const response = await fetch(
`http:
{
method: "DELETE" ,
}
);
if (!response.ok) {
throw new Error( "Failed to delete article" );
}
alert( "Article deleted" );
setDelete(!deleted);
} catch (error) {
console.error( "Error deleting article:" , error);
}
};
return (
<div id= "tbl-head" >
<h1>Articles</h1>
<table id= "tbl" >
<tr>
<th>Title</th>
<th>Category</th>
<th>Author</th>
<th>Date</th>
<th>Action</th>
</tr>
{articles.map((article) => (
<tr>
<td>{article.title}</td>
<td>{article.category}</td>
<td>{article.author}</td>
<td>{article.createdAt}</td>
<td>
<button
className= "dl-btn"
onClick={() => handleDelete(article._id)}
>
Delete
</button>
</td>
</tr>
))}
</table>
</div>
);
};
export default DeleteArticle;
|
Javascript
import React, { useState } from 'react' ;
function NewArticleForm({ setAuther }) {
const [formData, setFormData] = useState({
title: '' ,
author: '' ,
content: '' ,
category: ''
});
const handleChange = event => {
setFormData({
...formData,
[event.target.name]: event.target.value
});
};
const handleSubmit = async event => {
event.preventDefault();
try {
const response = await
method: 'POST' ,
headers: {
'Content-Type' : 'application/json'
},
body: JSON.stringify(formData)
});
if (!response.ok) {
throw new Error( 'Failed to create article' );
}
setFormData({
title: '' ,
author: '' ,
content: '' ,
category: ''
});
alert( "Article Added.." )
setAuther( false )
} catch (error) {
console.error( 'Error creating article:' , error);
}
};
return (
<div> { }
<div className= "form-container" >
<h2>Add New Article</h2>
<form onSubmit={handleSubmit}>
<div className= "form-group" >
<label >
Title:
<input
type= "text"
name= "title"
value={formData.title}
onChange={handleChange}
className= "form-input"
/>
</label>
</div>
<div className= "form-group" >
<label>
Author:
<input
type= "text"
name= "author"
value={formData.author}
onChange={handleChange}
className= "form-input"
/>
</label>
</div>
<div className= "form-group" >
<label>
Content:
<textarea
name= "content"
value={formData.content}
onChange={handleChange}
className= "form-textarea"
/>
</label>
</div>
<div className= "form-group" >
<label>
Category:
<input
type= "text"
name= "category"
value={formData.category}
onChange={handleChange}
className= "form-input"
/>
</label>
</div>
<button type= "submit"
className= "submit-button" >
Add Article
</button>
</form>
</div>
</div>
);
}
export default NewArticleForm;
|
Javascript
import React, { useState, useEffect } from 'react' ;
function NewsList() {
const [articles, setArticles] = useState([]);
useEffect(() => {
.then(response => response.json())
.then(data => setArticles(data))
. catch (error =>
console.error( 'Error fetching articles:' , error));
}, []);
return (
<div>
<div className= "App" >
<div class= "container" >
{articles.map(article => (
<div class= "card" >
<div class= "card__body" >
<span class= "tag tag-green" >
{article.category}
</span>
<h4>{article.title}</h4>
<p>{article.content}</p>
</div>
<div class= "card__footer" >
<div class= "user" >
<div class= "user__info" >
<h5>{article.author}</h5>
<small>{article.createdAt}</small>
</div>
</div>
</div>
</div>
))}
</div>
</div>
</div>
);
}
export default NewsList;
|
Start your frontend application using the following command.
npm run dev
Output:
Output
- Output of data saved in Database:
Database output
Share your thoughts in the comments
Please Login to comment...