Blogging Platform using MERN Stack
The blogging platform is developed using the MERN (MongoDB, ExpressJS, ReactJS, NodeJS) stack, allowing users to create, read, update, and delete blog posts. It provides a user-friendly interface for managing blog content, including features like adding new posts and viewing existing posts.
Output Preview: Let us have a look at how the final output will look like.
Prerequisites:
Approach to Create Blogging Platfrom using MERN:
The project follows a client-server architecture, with the frontend developed using React.js and Material-UI components, and the backend implemented using Express.js and MongoDB for data storage.
- Fetching existing blog posts from the backend and displaying them on the frontend.
- Adding new blog posts through a form with title and content fields.
- Deleting blog posts by clicking on a delete button associated with each post.
Steps to Create the Backend Server:
Step 1: Create a directory for the project.
mkdir server
cd server
Step 2: Initialized the Express app and installing the required packages
npm init -y
Step 3: Install the necessary package in your server using the following command.
npm install express cors
Project Structure(Backend):
The updated dependencies in package.json file of backend will look like:
"dependencies": {
"cors": "^2.8.5",
"express": "^4.18.3",
"mongoose": "^8.2.1"
}
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const bodyParser = require('body-parser');
const app = express();
const PORT = process.env.PORT || 3000;
// MongoDB Connection
mongoose.connect('mongodb://localhost:27017/blogDB',
{
useNewUrlParser: true,
useUnifiedTopology: true
});
const db = mongoose.connection;
db.on('error', console.error.bind(console,
'MongoDB connection error:'));
db.once('open', () => {
console.log('Connected to MongoDB');
});
app.use(cors());
// Post Model
const Post = mongoose.model('Post', {
title: String,
content: String,
createdAt: {
type: Date,
default: Date.now
},
updatedAt: { type: Date }
});
// Middleware
app.use(bodyParser.json());
// Routes
app.get('/posts', async (req, res) => {
try {
const posts = await Post.find();
res.json(posts);
} catch (err) {
res.status(500).json({
message: err.message
});
}
});
app.post('/posts', async (req, res) => {
const post = new Post({
title: req.body.title,
content: req.body.content
});
try {
const newPost = await post.save();
res.status(201).json(newPost);
} catch (err) {
res.status(400).json({ message: err.message });
}
});
app.put('/posts/:id', async (req, res) => {
try {
const updatedPost = await Post.findByIdAndUpdate(
req.params.id, {
title: req.body.title,
content: req.body.content,
updatedAt: Date.now()
}, { new: true });
res.json(updatedPost);
} catch (err) {
res.status(400).json({
message: err.message
});
}
});
app.delete('/posts/:id', async (req, res) => {
try {
await Post.findByIdAndDelete(req.params.id);
res.json({ message: 'Post deleted' });
} catch (err) {
res.status(500).json({
message: err.message
});
}
});
// Start Server
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
Start your server using the following command.
node server.js
Steps to Setup Frontend with React:
Step 1: Create React App
npx create-react-app client
Step 2: Switch to the project directory
cd client
Step 3: Installing the required packages:
npm install @material-ui/icons @material-ui/core axios
Step 5: Implement components for displaying blog posts and adding new posts.Use Axios to communicate with the backend API endpoints.
Project Structure(Frontend):
The updated Dependencies in package.json file of frontend will look like:
"dependencies": {
"@material-ui/core": "^4.12.4",
"@material-ui/icons": "^4.11.3",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.6.7",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}
/* App.css */
body{
background-color: #efecec;
}
.app {
font-family: Arial, sans-serif;
background-color: #efecec;
}
.app-bar {
background-color: #62929a;
}
.container {
padding-top: 20px;
}
.card {
height: 100%;
padding: 2%;
display: flex;
flex-direction: column;
margin-bottom: 20px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
}
.card-content {
flex-grow: 1;
padding: 20px;
}
.card-content input,
.card-content textarea {
width: 100%;
margin-bottom: 10px;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
}
.card-content textarea {
resize: vertical;
min-height: 100px;
}
.add-post-button {
padding: 10px;
background-color: #62929a;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s;
}
.add-post-button:hover {
background-color: #507f86;
}
.post-title {
font-size: 20px;
font-weight: bold;
margin-bottom: 10px;
}
.post-content {
font-size: 16px;
}
.card-actions {
display: flex;
justify-content: space-between;
margin-top: 10px;
}
.card-actions button {
margin-left: 5px;
}
import React, {
useState,
useEffect
} from 'react';
import {
AppBar,
Toolbar,
Typography,
Container,
Grid,
Card,
CardContent,
TextField,
Button
} from '@material-ui/core';
import {
Add as AddIcon
} from '@material-ui/icons';
import axios from 'axios';
import './App.css'; // Import CSS file
const apiUrl = 'http://localhost:3000';
function App() {
const [posts, setPosts] = useState([]);
const [newPost, setNewPost] = useState({
title: '',
content: ''
});
useEffect(() => {
axios.get(`${apiUrl}/posts`)
.then(response => {
setPosts(response.data);
})
.catch(error => {
console.error('Error fetching posts:', error);
});
}, []);
const handleInputChange = (event) => {
const { name, value } = event.target;
setNewPost(prevState => ({
...prevState,
[name]: value
}));
};
const handleAddPost = () => {
axios.post(`${apiUrl}/posts`, newPost)
.then(response => {
setPosts(prevState => [...prevState,
response.data]);
setNewPost({ title: '', content: '' });
})
.catch(error => {
console.error('Error adding post:', error);
});
};
const handleDeletePost = (id) => {
axios.delete(`${apiUrl}/posts/${id}`)
.then(() => {
setPosts(prevState => prevState.filter(
post => post._id !== id));
})
.catch(error => {
console.error('Error deleting post:', error);
});
};
return (
<div className="app">
<AppBar position="static" className="app-bar">
<Toolbar>
<Typography variant="h6">
My Blog
</Typography>
</Toolbar>
</AppBar>
<Container maxWidth="lg" className="container">
<Grid container spacing={3}>
<Grid item xs={12} sm={6} md={4}>
<Card className="card">
<CardContent className="card-content">
<TextField
label="Title"
name="title"
value={newPost.title}
onChange={handleInputChange}
fullWidth
margin="normal"
/>
<TextField
label="Content"
name="content"
value={newPost.content}
onChange={handleInputChange}
multiline
fullWidth
margin="normal"
/>
</CardContent>
<Button
variant="contained"
color="primary"
startIcon={<AddIcon />}
className="add-post-button"
onClick={handleAddPost}
>
Add Post
</Button>
</Card>
</Grid>
{posts.map(post => (
<Grid key={post._id} item xs={12} sm={6} md={4}>
<Card className="card">
<CardContent className="card-content">
<Typography variant="h5"
className="post-title">
{post.title}
</Typography>
<Typography variant="body2"
className="post-content">
{post.content}
</Typography>
</CardContent>
<div className="card-actions">
<Button
variant="outlined"
color="primary"
onClick={
() => handleDeletePost(post._id)}
>
Delete
</Button>
{/* Update button can be added similarly */}
</div>
</Card>
</Grid>
))}
</Grid>
</Container>
</div>
);
}
export default App;
Start your application using the following command.
npm start
Output :
This project provides a comprehensive solution for building a blogging platform using the MERN stack. It offers basic and advanced functionalities for managing blog content efficiently. Whether you’re a beginner or an experienced developer, this project serves as a valuable resource for learning and implementing full-stack web development concepts.