Open In App

Blogging Platform using MERN Stack

Last Updated : 15 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

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.

Screenshot-2024-03-12-183233

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):

Screenshot-2024-03-12-183733

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"
}
Javascript
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):

Screenshot-2024-03-12-183801

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"
}
CSS
/* 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;
}
Javascript
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 :

535-ezgifcom-video-to-gif-converter

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.



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads