Community Forum Page using MERN Stack
Last Updated :
09 Jan, 2024
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.
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:
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);
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' });
}
});
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'
}
);
}
});
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'
}
);
}
});
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'
}
);
}
});
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:
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
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
import React, { useEffect, useState } from 'react' ;
import axios from 'axios' ;
import RequestForm from './RequestForm' ;
import './RequestList.css' ;
const RequestList = () => {
const [requests, setRequests] = useState([]);
const fetchRequests = async () => {
try {
const response =
setRequests(response.data);
} catch (error) {
console.error( 'Error fetching requests:' , error);
}
};
useEffect(() => {
fetchRequests();
}, []);
const handleLike = async (id) => {
try {
await
axios.patch(`
http:
`);
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
import React, { useState } from 'react' ;
import axios from 'axios' ;
import './RequestForm.css' ;
const RequestForm = ({ onAddRequest }) => {
const [residentName, setResidentName] = useState( '' );
const [content, setContent] = useState( '' );
const handleSubmit = async (e) => {
e.preventDefault();
try {
const response =
residentName,
content,
});
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
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>
{ }
</AboutContainer>
);
};
export default About;
|
Javascript
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>
{ }
</div>
</NavbarContainer>
);
};
export default Navbar;
|
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
.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:
Output of Data Saved in Database:
Share your thoughts in the comments
Please Login to comment...