0
votes

MERN Stack/REDUX -- Attempting to send user data for signup/registration from my React app to express and into my mongoDB but keep receiving a 404 error in letting me know that the page doesnt exist. I've been able to connect it before on a previous application but having trouble now.

Server.js file:

const express = require("express");
const connectDB = require("./config/db");
const path = require("path");
const bodyParser = require("body-parser");

const app = express();

//Connecting to the DB \\
connectDB();

// Middleware Init\\
app.use(express.json({ extended: false }));
app.use(bodyParser.json());

// Routes \\
app.use("/api/users", require("./routes/users"));
app.use("/api/auth", require("./routes/auth"));

// Static assets if in production (deployment purposes) \\
if (process.env.NODE_ENV === "production") {
// Set static folder \\
app.use(express.static("client/build"));

app.get("*", (req, res) => {
    res.sendFile(path.join(__dirname, "client", "build", "index.html"));
});
}

const PORT = process.env.PORT || 5000;

app.listen(PORT, () => console.log(`Server started on port ${PORT}`));


   

Users Route:

const express = require("express");
const router = express.Router();
const { check, validationResult } = require("express-validator");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const config = require("config");
const auth = require("../middleware/auth");

//importing User Schema \\

const User = require("../models/User");

// @route   POST api/users
// @desc    Register user
// @access  Public
router.post(
    "/", [
        check("firstname", "First Name is Required").not().isEmpty(),
        check("lastname", "Last Name is Required").not().isEmpty(),
        check("email", "Email is Required").isEmail(),
        check(
            "password",
            "Please enter a password with 5 or more character"
        ).isLength({ min: 5 }),
    ],
async(req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
        return res.status(400).json({ errors: errors.array() });
    }
    // just deconstruct firstname, lastname, email, and password, isAdmin from req.body \\
    const { firstname, lastname, email, password, isAdmin } = req.body;

    try {
        // see if user exists \\
        let user = await User.findOne({ email });

        if (user) {
            return res
                .status(400)
                .json({ errors: [{ msg: "User already exists" }] });
        }

        user = new User({
            firstname,
            lastname,
            email,
            password,
            isAdmin,
        });

        // Encrypt password \\
        const salt = await bcrypt.genSalt(10);
        user.password = await bcrypt.hash(password, salt);

        await user.save();

        // Return jsonwebtoken
        const payload = {
            user: {
                id: user.id,
            },
        };

        // Login good for 1 hour \\
        jwt.sign(
            payload,
            config.get("jwtSecret"), { expiresIn: 36000 },
            (err, token) => {
                if (err) throw err;
                res.json({ token });
            }
        );
    } catch (err) {
        console.error(err.message);
        res.status(500).send("Server Error");
    }
}
);

// @route   PUT api/users/address
// @desc    Add/edit address
// @access  Private

router.put(
    "/address", [
        auth, [
            check("street", "Street is required").not().isEmpty(),
            check("country", "Country is required").not().isEmpty(),
            check("city", "City is required").not().isEmpty(),
            check("state", "State/Province is required").not().isEmpty(),
            check("zipcode", "Zip/Postal Code is required").not().isEmpty(),
        ],
    ],
    async(req, res) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            return res.status(400).json({ errors: errors.array() });
        }

        const { street, country, city, state, zipcode } = req.body;

        const newAddress = {
            street,
            country,
            city,
            state,
            zipcode,
        };

        try {
            // look for the user with the id and then update the address
            await User.updateOne({ _id: req.user.id }, {
                $set: {
                    address: newAddress,
                },
            });

            res.json({ msg: "Successfully saved your address." });
        } catch (err) {
            console.error(err.message);
            res.status(500).send("Server Error");
        }
    }
);
module.exports = router;

DB Configuration(db.js):

const mongoose = require("mongoose");
const config = require("config");
const db = config.get("mongoURI");

const connectDB = async() => {
    try {
        await mongoose.connect(db, {
            useNewUrlParser: true,
            useCreateIndex: true,
            useFindAndModify: false,
            useUnifiedTopology: true,
        });
        console.log("MongoDB Connected!..");
    } catch (err) {
        console.error(err.message);

        //exit process with failure\\
        process.exit(1);
    }
};

module.exports = connectDB;

DB configuration(default.json)

{
"mongoURI": "mongodb+srv://AJ****:*********@sustdropclust.zcc0v.mongodb.net/*******? 
retryWrites=true&w=majority",
"jwtSecret": "**********"

}

Middleware:

const jwt = require("jsonwebtoken");
const config = require("config");

module.exports = function(req, res, next) {
    // Get token from header \\
    const token = req.header("x-auth-token");

    // check if no token \\

    if (!token) {
        return res.status(401).json({ msg: "No token, auth denied" });
    }

    // Verify token \\
    try {
        const decoded = jwt.verify(token, config.get("jwtSecret"));

        req.user = decoded.user;
        next();
    } catch (err) {
        res.status(401).json({ msg: "Token not valid" });
    }
};

Redux Actions(auth.js):

import axios from "axios";
import { setAlert } from "./alert";
import {
    REGISTER_SUCCESS,
    REGISTER_FAIL,
    USER_LOADED,
    AUTH_ERROR,
    LOGIN_SUCCESS,
    LOGIN_FAIL,
    LOGOUT,
    MAKE_ADMIN,
    RESET_RECENT_ORDERS_LOADING,
} from "./types";
import setAuthToken from "../utilities/setAuthToken";

// Load User
export const loadUser = () => async(dispatch) => {
    if (localStorage.token) {
        setAuthToken(localStorage.token);
    }

    try {
        const res = await axios.get("/api/auth");
        if (res.data.isAdmin) {
            dispatch({ type: MAKE_ADMIN });
        }

        dispatch({
            type: USER_LOADED,
            payload: res.data,
        });
    } catch (err) {
        dispatch({
            type: AUTH_ERROR,
        });
    }
};

// Register User
export const register = ({ firstname, lastname, email, password }) => async(
    dispatch
) => {
    const config = {
        headers: {
            "Content-Type": "application/json",
        },
    };

    const body = JSON.stringify({ firstname, lastname, email, password });

    try {
        const res = await axios.post("/api/users", body, config);
        dispatch({
            type: REGISTER_SUCCESS,
            payload: res.data,
        });

        dispatch(loadUser());
        dispatch(setAlert("Registration is successful", "success"));
    } catch (err) {
        const errors = err.response.data.errors;

        if (errors) {
           errors.forEach((error) => dispatch(setAlert(error.msg, "danger")));
        }

        dispatch({
            type: REGISTER_FAIL,
        });
    }
};

// Login User
export const login = (email, password) => async(dispatch) => {
    const config = {
        headers: {
            "Content-Type": "application/json",
        },
    };

    const body = JSON.stringify({ email, password });

    try {
        const res = await axios.post("/api/auth", body, config);

        dispatch({
            type: LOGIN_SUCCESS,
            payload: res.data,
        });

        dispatch(setAlert("Welcome! Login Successful!", "success"));

        dispatch(loadUser());
    } catch (err) {
        const errors = err.response.data.errors;

        if (errors) {
            errors.forEach((error) => dispatch(setAlert(error.msg, "danger")));
        }

        dispatch({
            type: LOGIN_FAIL,
        });
    }
};

// Logout / Clear Profile
export const logout = () => (dispatch) => {
    dispatch({ type: LOGOUT });
    // Resets the loading for recent orders incase another user logs in without refreshing the page
    dispatch({ type: RESET_RECENT_ORDERS_LOADING });
};

// Add or Edit Address - Add address if it is null, edit it otherwise.
export const addAddress = ({ formData }) => async(dispatch) => {
    const config = {
        headers: {
            "Content-Type": "application/json",
        },
    };

    try {
        const res = await axios.put("/api/users/address", formData, config);

        dispatch(loadUser());
        dispatch(setAlert(res.data.msg, "success"));
        } catch (err) {
        const errors = err.response.data.errors;

        if (errors) {
            errors.forEach((error) => dispatch(setAlert(error.msg, "danger")));
        }
    }
};

Redux Reducers(auth.js):

import {
REGISTER_SUCCESS,
REGISTER_FAIL,
USER_LOADED,
AUTH_ERROR,
LOGIN_SUCCESS,
LOGIN_FAIL,
LOGOUT,
MAKE_ADMIN,
} from "../actions/types";

//add isAdmin later to authenticate if the current user is an admin (maybe just check user.isAdmin 
once we get the user and put it in the state)
const initialState = {
    token: localStorage.getItem("token"),
    isAuthenticated: false,
    loading: true,
    user: null,
    isAdmin: false,
};

export default function(state = initialState, action) {
    const { type, payload } = action;

    switch (type) {
        case USER_LOADED:
            return {
                ...state,
                isAuthenticated: true,
                loading: false,
                user: payload,
            };
        case REGISTER_SUCCESS:
        case LOGIN_SUCCESS:
            localStorage.setItem("token", payload.token);
            return {
                ...state,
                ...payload,
                isAuthenticated: true,
                loading: false,
            };
        case REGISTER_FAIL:
        case LOGIN_FAIL:
        case AUTH_ERROR:
        case LOGOUT:
            localStorage.removeItem("token");
            return {
                ...state,
                token: null,
                isAuthenticated: false,
                loading: false,
                isAdmin: false,
                user: null,
            };
        case MAKE_ADMIN:
            return {
                ...state,
                isAdmin: true,
            };
        default:
            return state;
    }
}

Registration page(register.js):

import React, { useState } from "react";
import { Link, Redirect } from "react-router-dom";
import { connect } from "react-redux";
import { setAlert } from "../../actions/alert";
import { register } from "../../actions/auth";
import PropTypes from "prop-types";

import "./Login.css";

const Register = ({ setAlert, register, isAuthenticated }) => {
const [formData, setFormData] = useState({
    firstname: "",
    lastname: "",
    email: "",
    password: "",
    password2: "",
});

const { firstname, lastname, email, password, password2 } = formData;

// it changes the value of the target every keystroke
const onChange = (e) =>
    setFormData({...formData, [e.target.name]: e.target.value });

const onSubmit = (e) => {
    e.preventDefault();
    if (password !== password2) {
        setAlert("Passwords do not match", "danger");
    } else {
        register({ firstname, lastname, email, password });
    }
};

// Redirects to /account after a successful registration
if (isAuthenticated) {
    return <Redirect to = "/account" / > ;
}

return ( 
    <div className = "wrapper-register" >
        <h1 className = "large text-dark" > create an account < /h1>

    <form onSubmit = {(e) => onSubmit(e)}>
    <div className = "form-group" >
        <label htmlFor = "firstname" > first name < /label> 
        <input className = "form-control" type = "text" name = "firstname" value = { firstname } onChange = {(e) => onChange(e)}/> 
        </div> 
        <div className = "form-group" >
    <label htmlFor = "lastname" > last name </label> 
    <input className = "form-control" type = "text" name = "lastname" value = { lastname } onChange = {(e) => onChange(e)}/> 
    </div> 
    <div className = "form-group" >
    <label htmlFor = "email" > email </label> 
    <input className = "form-control" type = "email" name = "email" value = { email } onChange = {(e) => onChange(e)}/> 
    </div> 
    <div className = "form-group" >
    <label htmlFor = "password" > password </label> 
    <input className = "form-control" type = "password" name = "password" value = { password } onChange = {(e) => onChange(e)} minLength = "6" />
    </div> 
    <div className = "form-group" >
    <label htmlFor = "password" > confirm password < /label> < input className = "form-control" type = "password" name = "password2" value = { password2 } onChange = {(e) => onChange(e)}minLength = "6" />
    </div> 
    <div className = "register-button" >
    <input type = "submit" className = "btn btn-dark btn-block" value = "Register" />
    </div> 
    </form > 
    <p>Already have an account ? < Link to = "/account/login" > Sign In </Link> </p> 
    </div>
    );
};

Register.propTypes = {
    setAlert: PropTypes.func.isRequired,
    register: PropTypes.func.isRequired,
};

const mapStateToProps = (state) => ({
    isAuthenticated: state.auth.isAuthenticated,
});

export default connect(mapStateToProps, { setAlert, register })(Register);
3
error 404 means there is no such router . i think you are sending request to port 3000 but server is running at port 5000 - sharun k k

3 Answers

0
votes

I noticed that you are running the server on port 5000, i guess that might have caused the error since you are sending a POST request to port 3000 instead. Unless you specified a port in process.env.PORT of 3000.

0
votes

Figured it out. When you create a react app through the create-react-app cli you have to specify and set your proxy in package.json like so "proxy": "http://localhost:4000". This way, when you fetch('/api/signup') in development, the development server will recognize that it’s not a static asset, and will proxy your request to http://localhost:4000/api/signup

0
votes

in Redux Actions(auth.js) you can set base URL path in axios defaults (just after imports).

window.axios.defaults.baseURL = (process.env.NODE_ENV !== 'production') ? 'your production server url' : 'http://localhost:5000/';