Login functionality using PERN

To implement a login functionality using React for the front-end, Node.js for the back-end, and Sequelize as the ORM (Object Relational Mapping) for interacting with a database (like PostgreSQL or MySQL), you can break down the process into several steps,

  1. Set up the Project Structure
    You'll need to create a full-stack application with separate folders for the client (React) and the server (Node.js with Sequelize).

     /project-root
       /client      # React front-end
       /server      # Node.js back-end with Sequelize
    
  2. Server-side Implementation
    i) Install dependencies
    First, you'll need to install the necessary packages for your Node.js server.

     npm init -y
     npm install express # Web framework for Node.js
     npm install sequelize sequelize-cli # ORM for database interaction 
     npm install mysql2 
     npm install bcryptjs # For password hashing
     npm install jsonwebtoken # For creating and verifying JWTs (JSON Web Tokens)
     npm install cors # Middleware to handle CORS (Cross-Origin Resource Sharing)
     npm install dotenv # To manage environment variables
    

    ii) Sequelize Setup
    Run the following commands to set up Sequelize and create your models.

     npx sequelize-cli init
     npx sequelize-cli model:generate --name User --attributes username:string,email:string,password:string
     npx sequelize-cli db:migrate
    

    This will create the Sequelize config, models, and migrations for the User model with the fields username, email, and password.
    iii) Create Express Server
    Create an index.js or app.js file in the server folder to set up the Express server.

     const express = require('express');
     const cors = require('cors');
     const bcrypt = require('bcryptjs');
     const jwt = require('jsonwebtoken');
     const { User } = require('./models');  // Import the User model
     require('dotenv').config();
    
     const app = express();
     app.use(express.json());
     app.use(cors());
    
     const PORT = process.env.PORT || 5000;
     const JWT_SECRET = process.env.JWT_SECRET || 'supersecretkey';
    
     // User Registration
     app.post('/register', async (req, res) => {
       const { username, email, password } = req.body;
    
       try {
         // Hash the password
         const hashedPassword = await bcrypt.hash(password, 10);
    
         // Create a new user
         const user = await User.create({ username, email, password: hashedPassword });
    
         res.status(201).json(user);
       } catch (error) {
         res.status(500).json({ error: 'Error registering user' });
       }
     });
    
     // User Login
     app.post('/login', async (req, res) => {
       const { email, password } = req.body;
    
       try {
         // Check if user exists
         const user = await User.findOne({ where: { email } });
    
         if (!user) {
           return res.status(400).json({ error: 'Invalid email or password' });
         }
    
         // Compare the provided password with the stored hashed password
         const isMatch = await bcrypt.compare(password, user.password);
    
         if (!isMatch) {
           return res.status(400).json({ error: 'Invalid email or password' });
         }
    
         // Generate JWT token
         const token = jwt.sign({ id: user.id, email: user.email }, JWT_SECRET, {
           expiresIn: '1h',
         });
    
         res.json({ token });
       } catch (error) {
         res.status(500).json({ error: 'Error logging in' });
       }
     });
    
     // Protected Route (example)
     app.get('/profile', authenticateToken, (req, res) => {
       res.json({ message: 'This is a protected route', user: req.user });
     });
    
     // Middleware for Token Authentication
     function authenticateToken(req, res, next) {
       const token = req.headers['authorization'];
    
       if (!token) return res.status(401).json({ error: 'Access Denied' });
    
       jwt.verify(token, JWT_SECRET, (err, user) => {
         if (err) return res.status(403).json({ error: 'Invalid Token' });
         req.user = user;
         next();
       });
     }
    
     app.listen(PORT, () => {
       console.log(`Server is running on port ${PORT}`);
     });
    

    iv) Environment Variables
    Create a .env file to store sensitive information like JWT secret and database credentials.

     JWT_SECRET=supersecretkey
    
  3. Client-side Implementation

    i) Install dependencies
    Inside the client folder, create a React project and install the required dependencies.

     npx create-react-app client
     cd client
     npm install axios # To make HTTP requests to the back-end
     npm install react-router-dom # To handle routing between different pages in React
    

    ii) Create the Login and Registration Forms
    In your React app, create Login.js and Register.js components to handle user login and registration.

     // src/Register.js
     import { useState } from 'react';
     import axios from 'axios';
    
     function Register() {
       const [username, setUsername] = useState('');
       const [email, setEmail] = useState('');
       const [password, setPassword] = useState('');
    
       const handleRegister = async (e) => {
         e.preventDefault();
         try {
           const response = await axios.post('http://localhost:5000/register', {
             username,
             email,
             password
           });
           console.log(response.data);
         } catch (error) {
           console.error('Error registering user', error);
         }
       };
    
       return (
         <div>
           <h2>Register</h2>
           <form onSubmit={handleRegister}>
             <input type="text" placeholder="Username" onChange={(e) => setUsername(e.target.value)} />
             <input type="email" placeholder="Email" onChange={(e) => setEmail(e.target.value)} />
             <input type="password" placeholder="Password" onChange={(e) => setPassword(e.target.value)} />
             <button type="submit">Register</button>
           </form>
         </div>
       );
     }
    
     export default Register;
    
     // src/Login.js
     import { useState } from 'react';
     import axios from 'axios';
    
     function Login() {
       const [email, setEmail] = useState('');
       const [password, setPassword] = useState('');
       const [token, setToken] = useState('');
    
       const handleLogin = async (e) => {
         e.preventDefault();
         try {
           const response = await axios.post('http://localhost:5000/login', { email, password });
           setToken(response.data.token); // Store token in state
         } catch (error) {
           console.error('Error logging in', error);
         }
       };
    
       return (
         <div>
           <h2>Login</h2>
           <form onSubmit={handleLogin}>
             <input type="email" placeholder="Email" onChange={(e) => setEmail(e.target.value)} />
             <input type="password" placeholder="Password" onChange={(e) => setPassword(e.target.value)} />
             <button type="submit">Login</button>
           </form>
           {token && <p>Your token: {token}</p>}
         </div>
       );
     }
    
     export default Login;
    

    iii) Protect Routes in React

    You can create a higher-order component to protect certain routes in React using the JWT token.

     // src/PrivateRoute.js
     import { Route, Redirect } from 'react-router-dom';
    
     const PrivateRoute = ({ component: Component, ...rest }) => {
       const isAuthenticated = !!localStorage.getItem('token');
    
       return (
         <Route
           {...rest}
           render={props =>
             isAuthenticated ? (
               <Component {...props} />
             ) : (
               <Redirect to="/login" />
             )
           }
         />
       );
     };
    
     export default PrivateRoute;
    
  4. Connecting Front-end with Back-end

    i) Ensure the React app is running on a different port (e.g., 3000) and that CORS is handled in the Node.js server (already included in the setup with cors()).

    ii) Use axios to make HTTP requests from the React components to the back-end API endpoints (/register and /login).

  5. Running the Application
    i) Run the back-end server

     cd server
     node app.js
    

    ii) Run the React front-end

     cd client
     npm start
    

    With this setup, you have a basic login and registration system using React, Node.js, and Sequelize. You can further extend it by adding features such as validation, error handling, and session management.