Open In App

Social Media Platform using MERN Stack

Last Updated : 16 Jan, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

In web development, creating a “Social Media Website” will showcase and utilising the power of MERN stack – MongoDB, Express, React, and Node. This application will provide users the functionality to add a post, like the post and able to comment on it.

Preview Image: Let us have a look at how the final output will look like.
wf

Prerequisites:

Approach to create Social Media Platform:

  • The social media website was developed with a dual focus on backend using Express.js and frontend using React.
  • Express handled API routes for CRUD operations, including likes and comments, and Multer facilitated file uploads for multimedia content.
  • React was chosen for the frontend, providing an interactive user interface with components for posts, likes, and comments.
  • Axios played a pivotal role in connecting the frontend to the backend API endpoints, ensuring smooth communication.
  • The integration phase involved configuring CORS, connecting frontend to backend API URLs, and thorough end-to-end testing for a seamless user experience.

Steps to Create the Project:

Step 1: Create a directory for the backend by running the following command.

npm init social_backend
cd social_backend

Step 2: Initialize the Express project and install the following dependencies.

npm init -y
npm install express mongoose cors body-parser multer uuid

Folder Structure(Backend):

ba

The updated dependencies in package.json file of backend will look like:

"dependencies": {
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.18.2",
"mongoose": "^8.0.3",
"multer": "^1.4.5-lts.1",
"uuid": "^9.0.1"
}

Example: Create the required files and add the following code:

Javascript




// models/Post.js
 
const mongoose = require('mongoose');
 
const postSchema = new mongoose.Schema({
    title: String,
    content: String,
    likes: { type: Number, default: 0 },
    comments: [{ text: String }],
});
 
const Post = mongoose.model('Post', postSchema);
 
module.exports = Post;


Javascript




// server.js
 
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const cors = require('cors');
const multer = require('multer');
const path = require('path');
 
const app = express();
const PORT = process.env.PORT || 5000;
 
app.use(cors());
app.use('/uploads', express.static(path.join(__dirname, 'uploads')));
 
const storage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, 'uploads/');
    },
    filename: function (req, file, cb) {
        cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname));
    }
});
 
const upload = multer({ storage: storage });
 
mongoose.connect('Your MongoDB connection string', { useNewUrlParser: true, useUnifiedTopology: true });
 
const postSchema = new mongoose.Schema({
    title: String,
    content: String,
    file: String,
    likes: { type: Number, default: 0 },
    comments: [{ text: String }],
});
 
const Post = mongoose.model('Post', postSchema);
 
app.use(bodyParser.json());
 
app.get('/api/posts', async (req, res) => {
    try {
        const posts = await Post.find();
        res.json(posts);
    } catch (error) {
        res.status(500).json({ error: 'Internal Server Error' });
    }
});
 
app.post('/api/posts', upload.single('file'), async (req, res) => {
    try {
        const { title, content } = req.body;
        const file = req.file ? req.file.filename : undefined;
 
        if (!title || !content) {
            return res.status(400).json({ error: 'Title and content are required fields' });
        }
 
        const post = new Post({ title, content, file });
        await post.save();
        res.status(201).json(post);
    } catch (error) {
        console.error('Error creating post:', error);
        res.status(500).json({ error: 'Internal Server Error' });
    }
});
 
app.post('/api/posts/like/:postId', async (req, res) => {
    try {
        const postId = req.params.postId;
        const post = await Post.findById(postId);
 
        if (!post) {
            return res.status(404).json({ error: 'Post not found' });
        }
 
        post.likes += 1;
        await post.save();
 
        res.json(post);
    } catch (error) {
        console.error('Error liking post:', error);
        res.status(500).json({ error: 'Internal Server Error' });
    }
});
 
app.post('/api/posts/comment/:postId', async (req, res) => {
    try {
        const postId = req.params.postId;
        const { text } = req.body;
        const post = await Post.findById(postId);
 
        if (!post) {
            return res.status(404).json({ error: 'Post not found' });
        }
 
        post.comments.push({ text });
        await post.save();
 
        res.json(post);
    } catch (error) {
        console.error('Error adding comment:', error);
        res.status(500).json({ error: 'Internal Server Error' });
    }
});
 
app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});


Step 3: To start the backend run the following command.

node server.js

Step 4: Set up React frontend using the command.

npx create-react-app social_frontend
cd social_frontend

Step 5 : Installing the required packages:

npm i axios react-router-dom

Folder Structure(Frontend):

fr

The updated dependencies in package.json file of frontend will look lik:

"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.6.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.21.1",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}

Example: Create the required files and the following code.

Javascript




// App.js
 
import React from 'react';
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
import Home from './Home';
import CreatePost from './CreatePost';
import './App.css';
 
function App() {
    return (
        <Router>
            <div className="app">
                <nav>
                    <ul>
                        <li>
                            <Link to="/">Home</Link>
                        </li>
                        <li>
                            <Link to="/create">Create Post</Link>
                        </li>
                    </ul>
                </nav>
                <Routes>
                    <Route path="/create" element={<CreatePost />} />
                    <Route path="/" element={<Home />} />
                </Routes>
            </div>
        </Router>
    );
}
 
export default App;


Javascript




// Home.js
 
import React, { useState, useEffect } from "react";
import axios from "axios";
 
function Home() {
    const [commentInput, setCommentInput] = useState("");
    const [posts, setPosts] = useState([]);
 
    useEffect(() => {
        axios
            .get("http://localhost:5000/api/posts")
            .then((response) => setPosts(response.data))
            .catch((error) => console.error("Error fetching posts:", error));
    }, []);
 
    const handleLike = (postId) => {
        axios
            .post(`http://localhost:5000/api/posts/like/${postId}`)
            .then((response) => {
                const updatedPosts = posts.map((post) =>
                    post._id === postId ? response.data : post
                );
                setPosts(updatedPosts);
            })
            .catch((error) => console.error("Error liking post:", error));
    };
 
    const handleAddComment = (postId, commentText) => {
        axios
            .post(`http://localhost:5000/api/posts/comment/${postId}`, {
                text: commentText,
            })
            .then((response) => {
                const updatedPosts = posts.map((post) =>
                    post._id === postId ? response.data : post
                );
                setPosts(updatedPosts);
            })
            .catch((error) => console.error("Error adding comment:", error));
    };
 
    return (
        <div className="home">
            <h2>Recent Posts</h2>
            {posts.map((post) => (
                <div key={post._id} className="post">
                    <h3>{post.title}</h3>
                    <p>{post.content}</p>
                    {post.file && (
                        <div>
                            {post.file.includes(".mp4") ? (
                                <video width="320" height="240" controls>
                                    <source
                                        src={
                                    `http://localhost:5000/uploads/${post.file}`
                                        }
                                        type="video/mp4"
                                    />
                                    Your browser does not support the video tag.
                                </video>
                            ) : (
                                <img
                                    src={
                                    `http://localhost:5000/uploads/${post.file}`
                                    }
                                    alt="Post Media"
                                />
                            )}
                        </div>
                    )}
                    <p>Likes: {post.likes}</p>
                    <button onClick={() => handleLike(post._id)}>Like</button>
                    <p>Comments: {post.comments.length}</p>
                    <ul>
                        {post.comments.map((comment, index) => (
                            <li key={index}>{comment.text}</li>
                        ))}
                    </ul>
 
                    <input
                        type="text"
                        placeholder="Add a comment"
                        className="comment-input"
                        onChange={(e) => setCommentInput(e.target.value)}
                    />
                    <button
                        onClick={() => handleAddComment(post._id, commentInput)}
                        className="comment-button"
                    >
                        Add Comment
                    </button>
                </div>
            ))}
        </div>
    );
}
 
export default Home;


Javascript




// CreatePost.js
import React, { useState } from "react";
import axios from "axios";
 
function CreatePost() {
    const [newPost, setNewPost] = useState({
        title: "",
        content: "",
        file: null,
    });
 
    const handleInputChange = (event) => {
        const { name, value } = event.target;
        setNewPost({ ...newPost, [name]: value });
    };
 
    const handleFileChange = (event) => {
        setNewPost({ ...newPost, file: event.target.files[0] });
    };
 
    const handlePostSubmit = () => {
        const formData = new FormData();
        formData.append("title", newPost.title);
        formData.append("content", newPost.content);
        formData.append("file", newPost.file);
 
        axios
            .post("http://localhost:5000/api/posts", formData)
            .then((response) => {
                setNewPost({ title: "", content: "", file: null });
            })
            .catch((error) => console.error("Error creating post:", error));
    };
 
    return (
        <div className="create-post">
            <h2>Create a Post</h2>
            <input
                type="text"
                name="title"
                placeholder="Title"
                value={newPost.title}
                onChange={handleInputChange}
            />
            <textarea
                name="content"
                placeholder="Content"
                value={newPost.content}
                onChange={handleInputChange}
            ></textarea>
            <input type="file" name="file" onChange={handleFileChange} />
            <button onClick={handlePostSubmit}>Post</button>
        </div>
    );
}
 
export default CreatePost;


CSS




/* App.css */
 
.home {
    max-width: 800px;
    margin: 0 auto;
}
 
.post {
    border: 1px solid #ddd;
    padding: 15px;
    margin-bottom: 20px;
}
 
.post h3 {
    color: #333;
}
 
.post p {
    color: #555;
}
 
/* App.css */
 
.create-post {
    max-width: 600px;
    margin: 20px auto;
    padding: 20px;
    border: 1px solid #ddd;
    background-color: #fff;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
 
.create-post h2 {
    color: #333;
}
 
.create-post input,
.create-post textarea {
    width: 100%;
    margin: 10px 0;
    padding: 10px;
}
 
.create-post button {
    background-color: #4caf50;
    color: #fff;
    padding: 10px 15px;
    border: none;
    cursor: pointer;
}
 
.comment-input {
    margin-top: 10px;
    padding: 8px;
    width: 70%;
}
 
.comment-button {
    background-color: #4caf50;
    color: #fff;
    padding: 8px 16px;
    border: none;
    cursor: pointer;
}
 
.post img,
.post video {
    max-width: 100%;
    height: auto;
    margin-top: 10px;
}
 
.post button {
    background-color: #4caf50;
    color: #fff;
    padding: 8px 16px;
    border: none;
    cursor: pointer;
    margin-right: 10px;
}
 
.post ul {
    list-style: none;
    padding: 0;
}
 
.post li {
    margin-bottom: 5px;
}
 
.comment-input {
    margin-top: 10px;
    padding: 8px;
    width: 70%;
}
 
.comment-button {
    background-color: #4caf50;
    color: #fff;
    padding: 8px 16px;
    border: none;
    cursor: pointer;
}
 
 
/* App.css */
 
.app {
    max-width: 800px;
    margin: 0 auto;
}
 
nav {
    background-color: #333;
    padding: 10px;
}
 
nav ul {
    list-style: none;
    margin: 0;
    padding: 0;
}
 
nav li {
    display: inline-block;
    margin-right: 20px;
}
 
nav a {
    text-decoration: none;
    color: #fff;
    font-weight: bold;
    font-size: 16px;
}
 
nav a:hover {
    color: #4caf50;
}
 
.create-post,
.home {
    border: 1px solid #ddd;
    padding: 20px;
    margin-bottom: 20px;
    background-color: #fff;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
 
.home h2,
.create-post h2 {
    color: #333;
}
 
.home .post,
.create-post {
    margin-bottom: 30px;
}
 
.home .post button,
.create-post button {
    background-color: #4caf50;
    color: #fff;
    padding: 10px 15px;
    border: none;
    cursor: pointer;
}
 
.home .post button:hover,
.create-post button:hover {
    background-color: #45a049;
}


Step 6: Start the application by running the following command.

npm start

Output:

vgh



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

Similar Reads