Search code examples
node.jsreactjsherokuserverbackend

Backend is not working in Heroku after deployment


Backend is working on the localhost:5000 but it doesn't work when I upload the backend on Heroku. I saw many videos on YouTube to see how to run the backend online on Heroku but none of the video helped me. I am a newbie I have started from scratch. I am giving my backend code. please check and tell me where I am wrong.

index.js

const connectToMongo = require('./db'); // Get the access from ('./db') to access the connectToMongo() function that is created in that component
const express = require('express')
require("dotenv").config();
var cors = require('cors')

// Call the connectToMongo() function in main file to access the database by the backend
connectToMongo();
const app = express()
// I can change the port anytime so I can't get confuse in different files when I run them 
const port = process.env.PORT || 5000;

app.use(cors())
// To send the JSON body request to database and read it in database
app.use(express.json())

// Available Routes
app.use('/api/auth', require('./routes/auth'))
app.use('/api/notes', require('./routes/notes'))

// Validation that the port is running
app.listen(port, () => {
  console.log(`iNotebook backend listening on port ${port}`)
})

This is my backend-end

router.post(
  "/login",
  [
    body("email", "Enter a valid email").isEmail(),
    body("password", "Password cannot be blank").exists(),
  ],
  async (req, res) => {
    let success = false;
    // If there are errors return bad request and the errors
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }

    const { email, password } = req.body;
    try {
      let user = await User.findOne({ email });
      if (!user) {
        return res
          .status(400)
          .json({ success, error: "Please try to login with correct credentials" });
      }

      const passwordCompare = await bcrypt.compare(password, user.password);
      if (!passwordCompare) {
        return res
          .status(400)
          .json({ success, error: "Please try to login with correct credentials" });
      }

      const data = {
        user: {
          id: user.id,
        },
      };
      const authtoken = jwt.sign(data, JWT_SECRET);
      // console.log(authtoken);
      success = true;
      res.json({ success, authtoken });

    } catch (error) {
      console.error(error.message);
      res.status(500).send("Internal Server Error");
    }
  }
);

package.json

{
  "name": "inotebook-backend",
  "version": "1.0.0",
  "description": "iNotebook - Your notebook on the cloud",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "bcryptjs": "^2.4.3",
    "cors": "^2.8.5",
    "dotenv": "^16.0.1",
    "express": "^4.17.3",
    "express-validator": "^6.14.0",
    "jsonwebtoken": "^8.5.1",
    "mongoose": "^6.2.9"
  },
  "devDependencies": {
    "nodemon": "^2.0.15"
  }
}

login.js front-end

import React, { useState } from 'react'
import { useNavigate } from 'react-router-dom'

const Login = (props) => {
  const [credentials, setCredentials] = useState({ email: "", password: "" })
  let navigate = useNavigate();

  const handleSubmit = async (e, email, password) => {
    e.preventDefault();
    const response = await fetch("http://localhost:5000/api/auth/login", {
      method: 'POST', // *GET, POST, PUT, DELETE, etc.
      headers: {
        'Content-Type': 'application/json',
        // 'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: JSON.stringify({ email: credentials.email, password: credentials.password }) // body data type must match "Content-Type" header
    });
    const json = await response.json();
    console.log(json);
    if (json.success) {
      //   Save the auth token and redirect
      localStorage.setItem('token', json.authtoken);
      // navigate("/") page will be redirected to this location after login
      navigate("/ShowNotes");
      props.showAlert("Logged in Successfully!", "success ");

    }
    else {
      props.showAlert("Invalid Credenetails", "danger");
    }
  }

  const onChange = (e) => {
    setCredentials({ ...credentials, [e.target.name]: e.target.value })
  }
  return (
    <>
      <div className="loginBody">
        <div className="loginHead">
          <div className="loginSection">
            <h2 className='loginHeading'>Login</h2>
          </div>
          <div className="loginBox">
            {/* <div className='loginPage'> */}
            <form className='loginForm' onSubmit={handleSubmit}>
              <div className="mb-3 my-2">
                <label htmlFor="email" className="form-label login-email-label">Email address :</label>
                <input type="email" className="email-input" id="email" name="email" onChange={onChange} value={credentials.email} aria-describedby="emailHelp" />
                {/* <div id="emailHelp" className="form-text">We'll never share your email with anyone else.</div> */}
              </div>
              <div className="mb-3 my-2 ">
                <label htmlFor="password" className="form-label login-password-label">Password :</label>
                <input type="password" className="password-input" name="password" onChange={onChange} value={credentials.password} id="password" />
              </div>
              <div className="loginPageBtnSection">
                <button type="submit" className="loginPageBtn"><i className="fa-solid fa-arrow-right"></i></button>
              </div>
            </form>
            {/* </div> */}
          </div>
        </div>
      </div>
    </>
  )
}

export default Login

Solution

  • Your error is in this part:

        const response = await fetch("http://localhost:5000/api/auth/login", {
    

    You're calling localhost, which works on your local machine. If you're using Heroku, it's not localhost.

    Why: Heroku assigns your project a unique url/ domain. You could use this domain to access the backend from your login.js part. The domain is shown in your dashboard. Another point is the port - in your case 5000. This won't work, because Heroku assigns your backend project a "random" port with the env variable PORT as you can see in:

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

    You could change the login part to

        const response = await fetch("__REPLACE_WITH_YOUR_PROJECT_DOMAIN_FROM_HEROKU_DASHBOARD__/api/auth/login", {
    

    or

        const response = await fetch("/api/auth/login", {
    

    UPDATE

    Another thing is your routing: The grouped routes won't work this way. You have to group them for your API structure with routers.

    Do get your structure working, you could use this part:

    // Available Routes
    const apiRouter = express.Router();
    const authRouter = express.Router();
    const notesRouter = express.Router();
    authRouter.use("/auth", require('./routes/auth'));
    notesRouter.use("/notes", require('./routes/notes'));
    apiRouter.use(authRouter);
    apiRouter.use(notesRouter);
    app.use("/api", apiRouter);
    

    In your code it's starting at line 16 which you can replace with the code above and it should work. Right now, your grouped structure doesn't match the frontend structure of your URLs.