Open In App

Community Forum Page using MERN Stack

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

In the ever-expanding digital landscape, fostering meaningful connections within communities is paramount. The Community Forum Page project, developed using the MERN (MongoDB, Express, React, Node) stack, aims to provide a dynamic platform for users to engage in discussions, share valuable information, and cultivate a thriving community environment.

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

imresizer-1704628759793

Prerequisites:

Approach to create Community Forum Page:

  • Creating BackEnd for our Application
  • Creating FrontEnd for Application
  • Connecting It with MongoDB

Steps to Create the Backend:

Step 1: Create a directory for project

npm init backend

Step 2: Open project using the command

cd backend

Step 3: Installing the required packages

npm install express mongoose cors body-parser

Project Structure:

Screenshot-2567-01-04-at-000243

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

"dependencies": {
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.18.2",
"mongoose": "^8.0.0",
}

Explanation:

  • Imports: Express, Mongoose, body-parser, cors modules for web server, MongoDB connection, request body handling, and CORS.
  • Creates an Express app instance, specifies a port (from PORT environment variable or defaulting to 5000).
  • Middleware setup: Handles CORS and parses JSON request bodies using cors and body-parser.
  • Defines “Request” data structure with attributes like residentName, content, and likes using Mongoose.
  • API Endpoints: Create, Read, Update (like), and Delete operations for requests are defined.
  • Server start: The server starts, listens on the specified port, and logs an operational message once running.

Example: Create server.js (Express Server) as connection between frontEnd and BackEnd. Insert the following code to it :

Javascript




const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const cors = require('cors');
 
const app = express();
const PORT = process.env.PORT || 5000;
 
app.use(cors());
app.use(bodyParser.json());
mongoose.connect(
    {
        useNewUrlParser: true,
        useUnifiedTopology: true
    });
 
 
const requestSchema = new mongoose.Schema({
    residentName: { type: String, required: true },
    content: { type: String, required: true },
    likes: { type: Number, default: 0 },
});
 
const Request =
    mongoose.model('Request', requestSchema);
 
// Create a new request
app.post('/requests', async (req, res) => {
    try {
        const { residentName, content } = req.body;
        const newRequest =
            new Request({ residentName, content });
        const savedRequest = await newRequest.save();
        res.json(savedRequest);
    } catch (error) {
        res.status(500)
            .json({ error: 'Internal Server Error' });
    }
});
 
// Get all requests
app.get('/requests', async (req, res) => {
    try {
        const requests = await Request.find();
        res.json(requests);
    } catch (error) {
        res.status(500)
            .json(
                {
                    error: 'Internal Server Error'
                }
            );
    }
});
 
// Like a request
app.patch('/requests/:id/like', async (req, res) => {
    try {
        const { id } = req.params;
        const updatedRequest =
            await Request.findByIdAndUpdate(
                id,
                { $inc: { likes: 1 } },
                { new: true });
        res.json(updatedRequest);
    } catch (error) {
        res.status(500)
            .json(
                {
                    error: 'Internal Server Error'
                }
            );
    }
});
 
// Delete a request
app.delete('/requests/:id', async (req, res) => {
    try {
        const { id } = req.params;
        await Request.findByIdAndDelete(id);
        res.json(
            {
                message: 'Request deleted successfully'
            }
        );
    } catch (error) {
        res.status(500)
            .json(
                {
                    error: 'Internal Server Error'
                }
            );
    }
});
// Example: routes/requests.js
 
const router = express.Router();
 
 
app.listen(PORT,
    () =>
        console.log(
            `Server is running on port ${PORT}`
        )
);


Steps to Create the Frontend:

Step 1: Set up React frontend using the command.

npx create-react-app client

Step 2: Navigate to the project folder using the command.

cd client

Step 3: Installing the required packages:

npm i axios react-router-dom

Project Structure:

Screenshot-2567-01-04-at-000408

The updated dependencies in package.json for frontend will look like:

"dependencies": {
"axios": "^1.6.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.17.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}

Explanation:

  • Form component with a title (FormTitle) indicating it’s for creating requests.
  • Utilizes flexbox with a column direction and small gaps for a clean layout.
  • Includes two form fields: Resident Name (text input) and Request Content (textarea), both styled.
  • Utilizes the useState hook to manage state for residentName and content.
  • handleSubmit function prevents default form submission, creates a new request object, calls onAddRequest callback with the new request, and clears form fields.
  • Receives onAddRequest prop as a callback function for handling new request additions.
  • Uses axios for asynchronous GET request to http://localhost:5000/requests, storing data in the requests state.
  • handleLike function makes a PATCH request to update likes for a specific request, then refreshes the request list.
  • UI divided into two columns: left column has RequestForm for new requests, and right column displays a list of community requests.
  • Community requests show resident name, issue content, and a “Like” button for user interaction.

Javascript




// src/App.js
import React from "react";
import {
    BrowserRouter as Router,
    Route, Routes
} from "react-router-dom";
import RequestList
    from "./components/RequestList";
import RequestForm
    from "./components/RequestForm";
import About from "./components/About";
import Navbar from "./components/Navbar";
const App = () => {
    return (
        <Router>
            <Navbar />
            <Routes>
                <Route path="/"
                    element={<RequestList />} />
                <Route path="/about"
                    element={<About />} />
            </Routes>
        </Router>
    );
};
 
export default App;


Javascript




// src/components/RequestList.js
import React, { useEffect, useState } from 'react';
import axios from 'axios';
// Import the RequestForm component
import RequestForm from './RequestForm';
import './RequestList.css';
 
const RequestList = () => {
    const [requests, setRequests] = useState([]);
 
    const fetchRequests = async () => {
        try {
            const response =
                await axios.get('http://localhost:5000/requests');
            setRequests(response.data);
        } catch (error) {
            console.error('Error fetching requests:', error);
        }
    };
 
    useEffect(() => {
        fetchRequests();
    }, []);
 
    const handleLike = async (id) => {
        try {
            await
                axios.patch(`
http://localhost:5000/requests/${id}/like
                `);
            // Refresh the list of requests after liking
            fetchRequests();
        } catch (error) {
            console.error('Error liking request:', error);
        }
    };
 
    return (
        <div className="request-list-container">
            <RequestForm onAddRequest={
                (newRequest) =>
                    setRequests([newRequest, ...requests])
            } />
            <ul className="request-list">
                {requests.map((request) => (
                    <div key={request._id}
                        className="request-item">
                        <p className="resident-name">
                            {request.residentName}
                        </p>
                        <p className="request-content">
                            {request.content}
                        </p>
                        <p className="likes">
                            Likes: {request.likes}
                        </p>
                        <button className="like-button"
                            onClick=
                            {
                                () => handleLike(request._id)
                            }>
                            Like
                        </button>
                    </div>
                ))}
            </ul>
        </div>
    );
};
 
export default RequestList;


Javascript




// src/components/RequestForm.js
import React, { useState } from 'react';
import axios from 'axios';
import './RequestForm.css'; // Import the CSS file for styling
 
const RequestForm = ({ onAddRequest }) => {
    const [residentName, setResidentName] = useState('');
    const [content, setContent] = useState('');
 
    const handleSubmit = async (e) => {
        e.preventDefault();
 
        try {
            const response =
                await axios.post('http://localhost:5000/requests', {
                    residentName,
                    content,
                });
 
            // Assuming the backend returns the newly created request
            onAddRequest(response.data);
            setResidentName('');
            setContent('');
        } catch (error) {
            console.error('Error creating request:', error);
        }
    };
 
    return (
        <div className="request-form-container">
            <h2>Create a Request</h2>
            <form onSubmit={handleSubmit}>
                <div className="form-group">
                    <label htmlFor="residentName">
                        Resident Name:
                    </label>
                    <input
                        id="residentName"
                        type="text"
                        value={residentName}
                        onChange={
                            (e) =>
                                setResidentName(e.target.value)
                        }
                    />
                </div>
                <div className="form-group">
                    <label htmlFor="content">
                        Content:
                    </label>
                    <textarea
                        id="content"
                        value={content}
                        onChange={
                            (e) =>
                                setContent(e.target.value)
                        }
                    />
                </div>
                <button type="submit"
                    className="submit-button">
                    Submit Request
                </button>
            </form>
        </div>
    );
};
 
export default RequestForm;


Javascript




// src/components/About.js
import React from 'react';
import styled from 'styled-components';
 
const AboutContainer = styled.div`
  max-width: 800px;
  margin: 20px auto;
  padding: 20px;
  background-color: #fff;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  border-radius: 8px;
`;
 
const Title = styled.h2`
  color: #333;
  margin-bottom: 20px;
`;
 
const Paragraph = styled.p`
  line-height: 1.6;
  margin-bottom: 10px;
`;
 
const About = () => {
    return (
        <AboutContainer>
            <Title>About Community Forum</Title>
            <Paragraph>
                Welcome to the Community Forum, a place where
                residents can connect, share ideas, and support
                each other. This platform allows you to raise
                requests, express your opinions, and engage in
                discussions with fellow community members.
            </Paragraph>
            <Paragraph>
                We believe in fostering a sense of community
                and collaboration. Feel free to explore the
                forum, like and contribute to requests, and
                be an active participant in shaping our community.
            </Paragraph>
            {/* Add more information as needed */}
        </AboutContainer>
    );
};
 
export default About;


Javascript




// src/components/Navbar.js
import React from 'react';
import styled from 'styled-components';
import { Link } from 'react-router-dom';
 
const NavbarContainer = styled.div`
  background-color: #212e3d;
  padding: 15px;
  display: flex;
  justify-content: space-between;
  align-items: center;
`;
 
const Logo = styled.h1`
  color: #fff;
  margin: 0;
`;
 
const NavLink = styled(Link)`
  color: #fff;
  text-decoration: none;
  margin-left: 20px;
  font-weight: bold;
  transition: color 0.3s ease-in-out;
 
  &:hover {
    color: #0e87ea;
  }
`;
 
const Navbar = () => {
    return (
        <NavbarContainer>
            <Logo>Community Forum</Logo>
            <div>
                <NavLink to="/">Home</NavLink>
                <NavLink to="/about">About</NavLink>
                {/* Add more navigation links as needed */}
            </div>
        </NavbarContainer>
    );
};
 
export default Navbar;


CSS




/* src/components/RequestForm.css */
 
.request-form-container {
    width: 600px;
    padding: 100px;
    border: 1px solid #ddd;
    border-radius: 8px;
    background-color: #f9f9f9;
    margin: 2%;
}
 
.form-group {
    margin-bottom: 15px;
}
 
label {
    display: block;
    font-weight: bold;
    margin-bottom: 5px;
}
 
input,
textarea {
    width: 100%;
    padding: 8px;
    border: 1px solid #ccc;
    border-radius: 4px;
}
 
.submit-button {
    background-color: #4caf50;
    color: #fff;
    padding: 10px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}


CSS




/* src/components/RequestList.css */
 
.request-list-container {
    padding: 20px;
    display: flex;
    flex-direction: row;
    border: 1px solid #ddd;
    border-radius: 8px;
    background-color: #f9f9f9;
}
 
.request-list {
    display: flex;
    flex-wrap: wrap;
    flex-direction: row;
    margin: 2%;
}
 
.request-item {
    margin: 1%;
 
    padding: 10px;
    width: 400px;
    border: 1px solid #ccc;
    border-radius: 4px;
    background-color: #fff;
}
 
.resident-name {
    font-weight: bold;
    margin-bottom: 5px;
}
 
.request-content {
    margin-bottom: 10px;
}
 
.likes {
    color: #888;
}
 
.like-button {
    background-color: #4caf50;
    color: #fff;
    padding: 5px 10px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}


Steps to run the Application:

node server.js and npm start

Output:

ezgifcom-video-to-gif-converter-(21)

Output of Data Saved in Database:

imresizer-1704628653045



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

Similar Reads