Create instagram clone app with proper state management USING MERN stack

 To create an Instagram clone app using the MERN stack (MongoDB, Express, React, and Node.js), I'll guide you through setting up the project, implementing proper state management with Redux for the frontend, and integrating it with a Node.js/Express backend connected to MongoDB.


Step-by-Step Guide to Building Instagram Clone using MERN Stack

Step 1: Set Up the Backend (Node.js + Express + MongoDB)

  1. Create Backend Folder:

    mkdir backend cd backend npm init -y npm install express mongoose cors body-parser multer jsonwebtoken bcryptjs dotenv
  2. Set Up Express Server (server.js)

    Create the server.js file to configure your backend:

    const express = require('express'); const mongoose = require('mongoose'); const cors = require('cors'); const bodyParser = require('body-parser'); require('dotenv').config(); const app = express(); const PORT = process.env.PORT || 5000; app.use(cors()); app.use(bodyParser.json()); mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true }) .then(() => console.log('MongoDB Connected')) .catch((err) => console.log(err)); app.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); });
  3. Create MongoDB Models:

    User Model (models/User.js):

    const mongoose = require('mongoose'); const bcrypt = require('bcryptjs'); const userSchema = new mongoose.Schema({ username: { type: String, required: true }, email: { type: String, required: true, unique: true }, password: { type: String, required: true }, avatar: { type: String }, }); // Hash the password before saving userSchema.pre('save', async function (next) { if (this.isModified('password')) { this.password = await bcrypt.hash(this.password, 10); } next(); }); const User = mongoose.model('User', userSchema); module.exports = User;

    Post Model (models/Post.js):

    const mongoose = require('mongoose'); const postSchema = new mongoose.Schema({ userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }, image: { type: String, required: true }, caption: { type: String }, createdAt: { type: Date, default: Date.now }, }); const Post = mongoose.model('Post', postSchema); module.exports = Post;
  4. Create Authentication Routes (routes/auth.js):

    Handle user registration and login with JWT.

    const express = require('express'); const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); const User = require('../models/User'); const router = express.Router(); // Register router.post('/register', async (req, res) => { const { username, email, password } = req.body; try { const existingUser = await User.findOne({ email }); if (existingUser) return res.status(400).json({ msg: 'User already exists' }); const hashedPassword = await bcrypt.hash(password, 10); const newUser = new User({ username, email, password: hashedPassword }); await newUser.save(); const token = jwt.sign({ id: newUser._id }, process.env.JWT_SECRET); res.json({ token, user: newUser }); } catch (err) { res.status(500).json({ msg: 'Server error' }); } }); // Login router.post('/login', async (req, res) => { const { email, password } = req.body; try { const user = await User.findOne({ email }); if (!user) return res.status(400).json({ msg: 'User not found' }); const isMatch = await bcrypt.compare(password, user.password); if (!isMatch) return res.status(400).json({ msg: 'Invalid credentials' }); const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET); res.json({ token, user }); } catch (err) { res.status(500).json({ msg: 'Server error' }); } }); module.exports = router;
  5. Create Post Routes (routes/post.js):

    Handle post creation, fetching posts, and file uploads.

    const express = require('express'); const multer = require('multer'); const Post = require('../models/Post'); const router = express.Router(); const storage = multer.diskStorage({ destination: (req, file, cb) => { cb(null, 'uploads/'); }, filename: (req, file, cb) => { cb(null, Date.now() + file.originalname); }, }); const upload = multer({ storage }); // Create Post router.post('/', upload.single('image'), async (req, res) => { const { caption, userId } = req.body; const image = req.file.path; const newPost = new Post({ caption, image, userId }); await newPost.save(); res.status(201).json(newPost); }); // Get Posts router.get('/', async (req, res) => { const posts = await Post.find().populate('userId', 'username avatar'); res.json(posts); }); module.exports = router;
  6. Link Routes to Express App:

    In server.js, import and use the authentication and post routes.

    const authRoutes = require('./routes/auth'); const postRoutes = require('./routes/post'); app.use('/api/auth', authRoutes); app.use('/api/posts', postRoutes);

Step 2: Set Up Frontend (React)

  1. Create React App: In the root directory, create a React app:

    npx create-react-app instagram-clone cd instagram-clone
  2. Install Dependencies: Install Redux, React Router, Axios for API calls, and Material-UI for UI components:

    npm install redux react-redux react-router-dom axios @mui/material @emotion/react @emotion/styled
  3. Set Up Redux for State Management:

    Create Redux store (src/store.js):

    import { createStore, combineReducers } from 'redux'; import { userReducer } from './reducers/userReducer'; const rootReducer = combineReducers({ user: userReducer, }); const store = createStore(rootReducer); export default store;

    Create Reducer (src/reducers/userReducer.js):

    const initialState = { user: null }; export const userReducer = (state = initialState, action) => { switch (action.type) { case 'SET_USER': return { ...state, user: action.payload }; default: return state; } };
  4. Create Actions (src/actions/userActions.js):

    export const setUser = (user) => { return { type: 'SET_USER', payload: user, }; };
  5. Create Components:

    • Login Component (src/components/Login.js):

      import React, { useState } from 'react'; import axios from 'axios'; import { useDispatch } from 'react-redux'; import { setUser } from '../actions/userActions'; const Login = () => { const dispatch = useDispatch(); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const handleLogin = async () => { try { const res = await axios.post('http://localhost:5000/api/auth/login', { email, password }); dispatch(setUser(res.data.user)); localStorage.setItem('token', res.data.token); } catch (err) { console.error('Error logging in', err); } }; return ( <div> <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} /> <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} /> <button onClick={handleLogin}>Login</button> </div> ); }; export default Login;
    • Post Feed Component (src/components/PostFeed.js):

      import React, { useEffect, useState } from 'react'; import axios from 'axios'; const PostFeed = () => { const [posts, setPosts] = useState([]); useEffect(() => { const fetchPosts = async () => { const res = await axios.get('http://localhost:5000/api/posts'); setPosts(res.data); }; fetchPosts(); }, []); return ( <div> {posts.map((post) => ( <div key={post._id}> <img src={`http://localhost:5000/${post.image}`} alt={post.caption} /> <p>{post.caption}</p> <p>{post.userId.username}</p> </div> ))} </div> ); }; export default PostFeed;
  6. Set Up Routing:

    In src/App.js, configure React Router:

    import React from 'react'; import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; import PostFeed from './components/PostFeed'; import Login from './components/Login'; const App = () => { return ( <Router> <Switch> <Route exact path="/" component={PostFeed} /> <Route path="/login" component={Login} /> </Switch> </Router> ); }; export default App;

Step 3: Run the App

  1. Backend:

    cd backend node server.js
  2. Frontend:

    cd instagram-clone npm start

Conclusion

You've now built the basic structure of an Instagram clone app using the MERN stack. You have a React frontend with Redux for state management, integrated with an Express.js backend and MongoDB. This structure allows users to register, log in, view posts, and upload images, with proper state management handling user data and posts efficiently.

Comments

Popular posts from this blog

PrimeNG tutorial with examples using frequently used classes

Docker and Kubernetes Tutorials and QnA

oAuth in angular