In this comprehensive guide, we will create a full-stack portfolio application with a dashboard. Using the MERN stack (MongoDB, Express, React, and Node.js), we’ll also incorporate Redux for state management. This application will showcase portfolio data, including personal details, skills, and projects. Let’s dive in!
1. Backend Setup
The backend of our application will use Node.js with Express.js for API routes and MongoDB for the database.
Setting up the Express Server
We’ll begin by setting up an Express server to handle our API routes and manage communication with the MongoDB database.
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
require('dotenv').config();
const app = express();
app.use(cors());
app.use(express.json());
mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log('MongoDB connected'))
.catch(err => console.error(err));
const portfolioRoutes = require('./routes/portfolioRoutes');
app.use('/api/portfolio', portfolioRoutes);
app.listen(5000, () => console.log(`Server running on port 5000`));
Explanation
- CORS Middleware: Enables cross-origin requests, allowing the frontend and backend to communicate seamlessly.
-
MongoDB Connection: The
mongoose.connect()
function connects to the MongoDB database using the URI from.env
. -
Routes: API routes are defined in
portfolioRoutes
and prefixed with/api/portfolio
.
2. Portfolio Model
Define a Mongoose schema and model for storing portfolio details, such as name, email, skills, and projects.
const mongoose = require('mongoose');
const PortfolioSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true },
skills: [String],
projects: [
{
title: "String,"
description: "String,"
link: String,
},
],
}, { timestamps: true });
module.exports = mongoose.model('Portfolio', PortfolioSchema);
Explanation
- Schema Definition: Defines the structure of portfolio data with fields for name, email, skills, and an array of projects.
- Timestamps: Automatically tracks when each document is created or updated.
** 3. API Routes**
Set up routes to handle CRUD operations for portfolio data.
const express = require('express');
const Portfolio = require('../models/Portfolio');
const router = express.Router();
router.get('/', async (req, res) => {
const portfolios = await Portfolio.find();
res.json(portfolios);
});
router.post('/', async (req, res) => {
const portfolio = new Portfolio(req.body);
await portfolio.save();
res.status(201).json(portfolio);
});
module.exports = router;
Explanation
- GET Route: Fetches all portfolio entries from the database.
- POST Route: Allows new portfolio entries to be added.
4. Frontend Setup
Setting up the React Application
Run the following commands to set up a React app and install necessary dependencies.
npx create-react-app client
cd client
npm install axios redux react-redux @reduxjs/toolkit react-router-dom
Explanation
- Axios: Used for making API requests.
- Redux: Manages the state of the application.
5. Redux Slice
Create a slice for managing portfolio data and API interactions.
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';
export const fetchPortfolios = createAsyncThunk('portfolio/fetch', async () => {
const response = await axios.get('http://localhost:5000/api/portfolio');
return response.data;
});
const portfolioSlice = createSlice({
name: 'portfolio',
initialState: { data: [], loading: false },
extraReducers: (builder) => {
builder
.addCase(fetchPortfolios.pending, (state) => { state.loading = true; })
.addCase(fetchPortfolios.fulfilled, (state, action) => {
state.data = action.payload;
state.loading = false;
});
},
});
export default portfolioSlice.reducer;
Explanation
-
createAsyncThunk
: Handles asynchronous API calls and manages loading states. - Reducers: Updates the state based on API response.
6. Dashboard Component
Create a dashboard to display portfolio data fetched from the backend.
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchPortfolios } from '../features/portfolioSlice';
const Dashboard = () => {
const dispatch = useDispatch();
const { data, loading } = useSelector(state => state.portfolio);
useEffect(() => {
dispatch(fetchPortfolios());
}, [dispatch]);
if (loading) return <p>Loading...</p>;
return (
<div>
<h1>Portfolio Dashboard</h1>
{data.map(portfolio => (
<div key={portfolio._id}>
<h3>{portfolio.name}</h3>
<p>{portfolio.email}</p>
<ul>
{portfolio.skills.map((skill, idx) => (
<li key={idx}>{skill}</li>
))}
</ul>
</div>
))}
</div>
);
};
export default Dashboard;
Explanation
- useEffect: Fetch portfolio data when the component loads.
- Conditional Rendering: Displays a loading message while data is being fetched.
** 7. Profile with Image**
Add a profile image and application title to the frontend.
App.js
import React from 'react';
import Dashboard from './components/Dashboard';
import './App.css';
const App = () => {
return (
<div className="App">
<header className="App-header">
<img
src="https://i.ibb.co/ZNGx9M4/developer.jpg"
alt="Profile"
className="profile-image"
/>
<h1>My Full Stack Portfolio</h1>
</header>
<Dashboard />
</div>
);
};
export default App;
App.css
.App-header {
display: flex;
flex-direction: column;
align-items: center;
margin: 20px;
}
.profile-image {
border-radius: 50%;
width: 150px;
height: 150px;
margin-bottom: 20px;
}
Explanation
- Profile Section: Displays a profile image and title.
- CSS: Styles the header for better visual appeal.
8. Running the Application
Start Backend
node server.js
Start Frontend
npm start
Output
- The application will display a profile image, a title, and a dashboard listing portfolio details.
Conclusion
By following this guide, you’ve built a full-stack portfolio application with a dashboard using the MERN stack. This project demonstrates key concepts in backend development, API creation, frontend development with React, and state management with Redux. You can further expand the application by adding user authentication, advanced styling, or deploying it to a cloud platform. Happy coding!
Top comments (0)