MERN应用程序在Netlify和Heroku(CORS)上的问题

发布于 2025-01-22 20:01:50 字数 10300 浏览 0 评论 0原文

这是我的第一个问题,但我完全有NOO知道该怎么做:/我学习JavaScript技术。我已经写了我的MERN应用程序,在其中处理登录并注册功能。我的后端部署在Heroku,但客户端部署在Netlify上。一切都在本地工作正常,但是当我在部署到Heroku并Netlify后测试我的应用程序时,一切都可以,直到我尝试将请求发送到后端(例如在登录过程中)。我的请求是在呼吸暂停的时间为20-30秒,此后我收到了此内容的通讯 - “ 访问'https://pokemontrainer-app.herokuapp.herokuapp.com/auth/signin'从origin中访问xmlhttprequest 'https://pokemon-trainer-mern-app.netlify.app'已被CORS策略阻止:在请求的资源上不存在'access-control-allow-origin'标题。 ”。我一直在寻找解决方案。大多数情况下,我看到有关_redirects文件的信息,用于netlify的客户端构建文件夹。不幸的是,关于此问题的文档非常简短且不清楚。也许你们中的一个问题也有类似的问题并成功解决了吗?如果_redirects文件确实是解决方案,我可以要求简短的ifnormation我应该如何准备吗?

这是我的后端代码:

server.js文件:

const express = require('express');
const cors = require('cors');
const mongoose =  require('mongoose');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser')
const mainRoutes = require('./routes/main.js');
const signinSignupRoutes = require('./routes/signInSignUp.js');
const userTrainersRoutes = require('./routes/userTrainers.js');
require('dotenv').config({ path: './.env' });

const app = express();
const port = process.env.PORT || 8000;

console.log(process.env.FRONTEND_URI);

//------Express-------

app.use(bodyParser.json({limit: '500mb'}));
app.use(bodyParser.urlencoded({limit: '500mb', extended: true}));
app.use(cookieParser());
app.use(express.json());
app.use(express.urlencoded());
app.use(cors(
  {
    credentials: true,
    origin: 'https://pokemon-trainer-mern-app.netlify.app'
  })
);

app.use('/', mainRoutes);
app.use('/auth',  signinSignupRoutes);
app.use('/loggedUser', userTrainersRoutes);

//------Mongoose-------

const main = async() => {
  try {
    await mongoose.connect(`mongodb+srv://${process.env.USERS_USERNAME}:${process.env.USERS_API_KEY}@pokemon-app.2s1cy.mongodb.net/myFirstDatabase?retryWrites=true&w=majority`);
    console.log('Database connection works!')
  }catch(err) {
    console.log(err.message);
  }
}

main()
.then(()=> app.listen(port, () => {
  console.log(`Server works on port ${port}`);
}))
.catch(err => console.log(err.message));

signin& up.js文件:

const bcrypt = require('bcryptjs');
const Joi = require('joi');
const jwt = require('jsonwebtoken');
const {User, validation} = require('../models/user.js');

const getUsers = async (req, res) => {
  
  try {
    const users = await User.find();
    res.status(200).json(users);
  } catch(err) {
    res.status(404).json(err.message);
  }
}

const signUp = async(req, res) => {
  
  try{
    const {error} = validation(req.body);
    error && res.status(400).send({message: error.details[0].message});
    const user = await User.findOne({email: req.body.email});
    if(user) {
      res.status(409).send({message: 'User with this email already exists.'})
    } else {
      if(req.body.userName === "") {
        res.status(400).send({message: `Username field is empty`});
      } else if(req.body.password !== req.body.confirmPassword || req.body.password === "") {
        res.status(400).send({message: `Passwords aren't the same or password field is empty`});
      } else {
        const hashedPassword = await bcrypt.hash(req.body.password, 12);
        await User.create({email: req.body.email, userName: req.body.userName, password: hashedPassword});
        res.status(201).send({message: 'User registered succesfully!'});
      }
    }
  } catch(err) {
    res.status(500).send({message: 'Internal server error :('});
  }
}

const signIn = async(req, res) => {
  res.setHeader('Access-Control-Allow-Origin', 'https://pokemon-trainer-mern-app.netlify.app');

  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');

  res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');

  res.setHeader('Access-Control-Allow-Credentials', true);
  
  try{
    const {error} = signInValidation(req.body);
    error && res.status(400).send({message: error.details[0].message});
    const user = await User.findOne({email: req.body.email});
    !user && res.status(401).send({message: 'User with this email adress is not registered :('});
    const validatedPassword = await bcrypt.compare(req.body.password, user.password);
    !validatedPassword && res.status(401).send({message: 'Incorrect password :('});
    const token = await user.generateAuthToken(user._id, user.email);
    res.cookie('token', token, {
      maxAge: 7 * 24 * 60 * 60 * 1000,
      httpOnly: process.env.NODE_ENV === 'production' ? true : false,
      secure: false,
    }).status(200).send({message: 'Log in succesfully', userData: {userId:user._id, userName: user.userName, email: user.email, trainers: user.trainers, logged: true}});
  } catch(err) {
    console.log(err.message);
  }
}

const signInViaGoogle = async(req, res) => {
  
  try{
    const user = await User.findOne({email: req.body.email});
    !user && res.status(401).send({message:'You have to register your account with this email in this app'});
    const token = user.generateAuthToken(user._id, user.email);
    res.cookie('token', token, {
      maxAge: 7 * 24 * 60 * 60 * 1000,
      httpOnly: process.env.NODE_ENV === `${production}` ? true : false,
      secure: false,
    }).status(200).send({message: 'Log in succesfully', userData: {userId:user._id, userName: user.userName,email: user.email, trainers: user.trainers, logged: true}});
  } catch(err) {
    res.status(500).send({message: 'Internal server error :('});
  }
}

const logout = async(req, res) => {
  
  try {
    const {token} = req.cookies;
    token && res.clearCookie('token').send({message: 'Cookie cleared'});
  } catch(err) {
    res.status(500).send({message: 'Internal server error :('});
  }
}

const newSession = async(req, res) => {
  
  const {token} = req.cookies;
  !token ? res.status(200).send({cookie: false, logged: false}) : res.status(200).send({cookie: true, logged: true});
}

// validation for signIn

const signInValidation = (data) => {
  const JoiSchema = Joi.object({
    email: Joi.string().required().label('E-mail'),
    password: Joi.string().required().label('Password'),
  });
  return JoiSchema.validate(data);
}

module.exports = {getUsers, signUp, signIn, signInViaGoogle, logout, newSession}

客户端代码:

apihandling.js文件:

import axios from 'axios';
import { loginNativeUser, updateUserData, newSession } from '../actions/userActions.js'

const url = 'https://pokemontrainer-app.herokuapp.com';

const instance =  axios.create({
    baseUrl: url,
    withCredentials: true,
    credentials: 'include',
})

export const newSess = async (dispatch) => {
   await instance.get(`${url}/auth/newSession`)
   .then(res => {
       dispatch(newSession(res.data));
   })
   .catch(err => console.log(err.message));
}

export const signInByGoogle = async (userData, setError, history, dispatch) => {
    await instance.post(`${url}/auth/signin/google`, {
        email: userData.email,
    })
    .then(res => {
        setError(null);
        dispatch(loginNativeUser(res.data.userData));
        history.push('/');
    })
    .catch(err => {
        setError(err.response.data.message);
        history.push('/auth/signin');
        alert(err.response.data.message);
    })
}

export const signIn = async (formData, setError, history, dispatch) => {
    await instance.post(`${url}/auth/signin`, {
        password: formData.password,
        email: formData.email,
    })
    .then(res => { 
        setError(null);
        dispatch(loginNativeUser(res.data.userData));
        history.push('/');
    })
    .catch(err => {
        setError(err.response.data.message);
        history.push('/auth/signin');
        alert(err.response.data.message);
    });
}

export const signUp = async (formData, setError, history) => {
    await instance.post(`${url}/auth/signup`, {
        userName: formData.userName,
        password: formData.password,
        confirmPassword: formData.confirmPassword,
        email: formData.email,
    })
    .then(res => { 
        setError(null);
        history.push('/');
        alert('Registered succesfully')
    })
    .catch(err => {
        setError(err.response.data.message);
        history.push('/auth/signup');
        alert(err.response.data.message);
    });
}

export const cookieClear = async () => {
    await instance.get(`${url}/auth/deleteCookie`)
    .then(res => {
        console.log('Cookie cleared');
    })
    .catch(err => {
        console.log(err.response.data.message);
    });
}

export const addTrainer = async (userId, trainer) => {
    await instance.patch(`${url}/loggedUser/${userId}/addTrainer`, {
        userId: userId,
        trainer: trainer
    })
    .then(res => {
        alert('Trainer added');
    })
    .catch(err => {
        alert(err.response.data.message);
    });
}

export const removeTrainer = async (userId, trainerId) => {
    await instance.patch(`${url}/loggedUser/${userId}/${trainerId}/removeTrainer`, {
        userId: userId,
        trainerId: trainerId
    })
    .then(res => {
        alert(res.data.message);
    })
    .catch(err =>{
        alert(err.response.data.message);
    })
}

export const addPokemon = async(userId, trainerId, pokemon) => {
    await instance.patch(`${url}/loggedUser/${userId}/${trainerId}/${pokemon}/addPokemon`, {
        userId: userId,
        trainerId: trainerId,
        pokemon: pokemon
    })
    .then(res => {
        alert('Pokemon caught');
    })
    .catch((err) => {
        alert(err.response.data.message);
    })
}

export const updateData = async (userId, dispatch) => {
    await instance.post(`${url}/loggedUser/${userId}/updateData`, {
        userId: userId,
    })
    .then(res => {
        dispatch(updateUserData(res.data.userData));
    })
    .catch(err => {
        console.log(err.response.data.message);
    });
}

如果需要,我还可以发送带有代码的github链接。

预先感谢您的回答。

This my first question but I completely have noo idea what to do :/ I learn javascript technologies. I've written my MERN app where I handle login and register feature. My backend is deployed on heroku but client side is deployed on netlify. Everything is working fine locally, but when I test my app after deployment to heroku and netlify, everything is ok till I try send a request to my backend (for example during login process). My request is pending aproximately 20-30 sec and after this time I receive annoucement with this content - "Access to XMLHttpRequest at 'https://pokemontrainer-app.herokuapp.com/auth/signin' from origin 'https://pokemon-trainer-mern-app.netlify.app' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.". I've been looking for a solution. Most often I saw infos about _redirects file for client build folder for netlify. Unfortunately documentation is very short and unclear when it comes to this issue. Maybe one of you had a similar problem and resolved it with success? If _redirects file is really solution, can I ask for short ifnormation how I should prepare it?

this is my backend code:

server.js file:

const express = require('express');
const cors = require('cors');
const mongoose =  require('mongoose');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser')
const mainRoutes = require('./routes/main.js');
const signinSignupRoutes = require('./routes/signInSignUp.js');
const userTrainersRoutes = require('./routes/userTrainers.js');
require('dotenv').config({ path: './.env' });

const app = express();
const port = process.env.PORT || 8000;

console.log(process.env.FRONTEND_URI);

//------Express-------

app.use(bodyParser.json({limit: '500mb'}));
app.use(bodyParser.urlencoded({limit: '500mb', extended: true}));
app.use(cookieParser());
app.use(express.json());
app.use(express.urlencoded());
app.use(cors(
  {
    credentials: true,
    origin: 'https://pokemon-trainer-mern-app.netlify.app'
  })
);

app.use('/', mainRoutes);
app.use('/auth',  signinSignupRoutes);
app.use('/loggedUser', userTrainersRoutes);

//------Mongoose-------

const main = async() => {
  try {
    await mongoose.connect(`mongodb+srv://${process.env.USERS_USERNAME}:${process.env.USERS_API_KEY}@pokemon-app.2s1cy.mongodb.net/myFirstDatabase?retryWrites=true&w=majority`);
    console.log('Database connection works!')
  }catch(err) {
    console.log(err.message);
  }
}

main()
.then(()=> app.listen(port, () => {
  console.log(`Server works on port ${port}`);
}))
.catch(err => console.log(err.message));

signIn&Up.js file:

const bcrypt = require('bcryptjs');
const Joi = require('joi');
const jwt = require('jsonwebtoken');
const {User, validation} = require('../models/user.js');

const getUsers = async (req, res) => {
  
  try {
    const users = await User.find();
    res.status(200).json(users);
  } catch(err) {
    res.status(404).json(err.message);
  }
}

const signUp = async(req, res) => {
  
  try{
    const {error} = validation(req.body);
    error && res.status(400).send({message: error.details[0].message});
    const user = await User.findOne({email: req.body.email});
    if(user) {
      res.status(409).send({message: 'User with this email already exists.'})
    } else {
      if(req.body.userName === "") {
        res.status(400).send({message: `Username field is empty`});
      } else if(req.body.password !== req.body.confirmPassword || req.body.password === "") {
        res.status(400).send({message: `Passwords aren't the same or password field is empty`});
      } else {
        const hashedPassword = await bcrypt.hash(req.body.password, 12);
        await User.create({email: req.body.email, userName: req.body.userName, password: hashedPassword});
        res.status(201).send({message: 'User registered succesfully!'});
      }
    }
  } catch(err) {
    res.status(500).send({message: 'Internal server error :('});
  }
}

const signIn = async(req, res) => {
  res.setHeader('Access-Control-Allow-Origin', 'https://pokemon-trainer-mern-app.netlify.app');

  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');

  res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');

  res.setHeader('Access-Control-Allow-Credentials', true);
  
  try{
    const {error} = signInValidation(req.body);
    error && res.status(400).send({message: error.details[0].message});
    const user = await User.findOne({email: req.body.email});
    !user && res.status(401).send({message: 'User with this email adress is not registered :('});
    const validatedPassword = await bcrypt.compare(req.body.password, user.password);
    !validatedPassword && res.status(401).send({message: 'Incorrect password :('});
    const token = await user.generateAuthToken(user._id, user.email);
    res.cookie('token', token, {
      maxAge: 7 * 24 * 60 * 60 * 1000,
      httpOnly: process.env.NODE_ENV === 'production' ? true : false,
      secure: false,
    }).status(200).send({message: 'Log in succesfully', userData: {userId:user._id, userName: user.userName, email: user.email, trainers: user.trainers, logged: true}});
  } catch(err) {
    console.log(err.message);
  }
}

const signInViaGoogle = async(req, res) => {
  
  try{
    const user = await User.findOne({email: req.body.email});
    !user && res.status(401).send({message:'You have to register your account with this email in this app'});
    const token = user.generateAuthToken(user._id, user.email);
    res.cookie('token', token, {
      maxAge: 7 * 24 * 60 * 60 * 1000,
      httpOnly: process.env.NODE_ENV === `${production}` ? true : false,
      secure: false,
    }).status(200).send({message: 'Log in succesfully', userData: {userId:user._id, userName: user.userName,email: user.email, trainers: user.trainers, logged: true}});
  } catch(err) {
    res.status(500).send({message: 'Internal server error :('});
  }
}

const logout = async(req, res) => {
  
  try {
    const {token} = req.cookies;
    token && res.clearCookie('token').send({message: 'Cookie cleared'});
  } catch(err) {
    res.status(500).send({message: 'Internal server error :('});
  }
}

const newSession = async(req, res) => {
  
  const {token} = req.cookies;
  !token ? res.status(200).send({cookie: false, logged: false}) : res.status(200).send({cookie: true, logged: true});
}

// validation for signIn

const signInValidation = (data) => {
  const JoiSchema = Joi.object({
    email: Joi.string().required().label('E-mail'),
    password: Joi.string().required().label('Password'),
  });
  return JoiSchema.validate(data);
}

module.exports = {getUsers, signUp, signIn, signInViaGoogle, logout, newSession}

client side code:

apiHandling.js file:

import axios from 'axios';
import { loginNativeUser, updateUserData, newSession } from '../actions/userActions.js'

const url = 'https://pokemontrainer-app.herokuapp.com';

const instance =  axios.create({
    baseUrl: url,
    withCredentials: true,
    credentials: 'include',
})

export const newSess = async (dispatch) => {
   await instance.get(`${url}/auth/newSession`)
   .then(res => {
       dispatch(newSession(res.data));
   })
   .catch(err => console.log(err.message));
}

export const signInByGoogle = async (userData, setError, history, dispatch) => {
    await instance.post(`${url}/auth/signin/google`, {
        email: userData.email,
    })
    .then(res => {
        setError(null);
        dispatch(loginNativeUser(res.data.userData));
        history.push('/');
    })
    .catch(err => {
        setError(err.response.data.message);
        history.push('/auth/signin');
        alert(err.response.data.message);
    })
}

export const signIn = async (formData, setError, history, dispatch) => {
    await instance.post(`${url}/auth/signin`, {
        password: formData.password,
        email: formData.email,
    })
    .then(res => { 
        setError(null);
        dispatch(loginNativeUser(res.data.userData));
        history.push('/');
    })
    .catch(err => {
        setError(err.response.data.message);
        history.push('/auth/signin');
        alert(err.response.data.message);
    });
}

export const signUp = async (formData, setError, history) => {
    await instance.post(`${url}/auth/signup`, {
        userName: formData.userName,
        password: formData.password,
        confirmPassword: formData.confirmPassword,
        email: formData.email,
    })
    .then(res => { 
        setError(null);
        history.push('/');
        alert('Registered succesfully')
    })
    .catch(err => {
        setError(err.response.data.message);
        history.push('/auth/signup');
        alert(err.response.data.message);
    });
}

export const cookieClear = async () => {
    await instance.get(`${url}/auth/deleteCookie`)
    .then(res => {
        console.log('Cookie cleared');
    })
    .catch(err => {
        console.log(err.response.data.message);
    });
}

export const addTrainer = async (userId, trainer) => {
    await instance.patch(`${url}/loggedUser/${userId}/addTrainer`, {
        userId: userId,
        trainer: trainer
    })
    .then(res => {
        alert('Trainer added');
    })
    .catch(err => {
        alert(err.response.data.message);
    });
}

export const removeTrainer = async (userId, trainerId) => {
    await instance.patch(`${url}/loggedUser/${userId}/${trainerId}/removeTrainer`, {
        userId: userId,
        trainerId: trainerId
    })
    .then(res => {
        alert(res.data.message);
    })
    .catch(err =>{
        alert(err.response.data.message);
    })
}

export const addPokemon = async(userId, trainerId, pokemon) => {
    await instance.patch(`${url}/loggedUser/${userId}/${trainerId}/${pokemon}/addPokemon`, {
        userId: userId,
        trainerId: trainerId,
        pokemon: pokemon
    })
    .then(res => {
        alert('Pokemon caught');
    })
    .catch((err) => {
        alert(err.response.data.message);
    })
}

export const updateData = async (userId, dispatch) => {
    await instance.post(`${url}/loggedUser/${userId}/updateData`, {
        userId: userId,
    })
    .then(res => {
        dispatch(updateUserData(res.data.userData));
    })
    .catch(err => {
        console.log(err.response.data.message);
    });
}

If it is needed, I can also send a github link with code.

Thank you in advance for your answers.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

小糖芽 2025-01-29 20:01:52

您可以将中间件添加到Express应用中。
我今天碰巧为我的Express API编写了一些CORS配置。
对于您的参考(服务器端代码):

const corsMiddleware = (req, res, next) => {
  res = applyCorsHeaders(res);
  if (req.method === 'OPTIONS') {
    res.status(200).end()
    return
  }
  next()
}

const applyCorsHeaders = res => {
  res.setHeader('Access-Control-Allow-Credentials', true);
  res.setHeader('Access-Control-Allow-Origin', '*')
  // or res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
  res.setHeader('Access-Control-Allow-Methods', 'GET,OPTIONS,PATCH,DELETE,POST,PUT')
  res.setHeader(
    'Access-Control-Allow-Headers',
    'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version'
  )
  return res;
}

然后在应用程序中使用此中间件:

app.use(corsMiddleware);

有关CORS的更多信息,这是一个官方介绍: https://developer.mozilla.org/en-us/docs/web/http/cors

You can add a middleware into you express app.
I happened to write some cors config for my express api today.
For your reference (server side code):

const corsMiddleware = (req, res, next) => {
  res = applyCorsHeaders(res);
  if (req.method === 'OPTIONS') {
    res.status(200).end()
    return
  }
  next()
}

const applyCorsHeaders = res => {
  res.setHeader('Access-Control-Allow-Credentials', true);
  res.setHeader('Access-Control-Allow-Origin', '*')
  // or res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
  res.setHeader('Access-Control-Allow-Methods', 'GET,OPTIONS,PATCH,DELETE,POST,PUT')
  res.setHeader(
    'Access-Control-Allow-Headers',
    'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version'
  )
  return res;
}

Then use this middleware in your app:

app.use(corsMiddleware);

For more about CORS, here's one official intro: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

羞稚 2025-01-29 20:01:52

感谢您的所有回应。我决定将整个应用程序放在Heroku上。有用。 CORS政策问题消失了。问题与不同的领域有关(Heroku用于后端和Frontend的Netlify)。我决定使用此解决方案,因为我从未尝试过将正端放在Heroku上。它需要重新安排我的项目文件夹。现在我的项目结构是:

在此处输入图像说明

  1. 访问heroku.com
  2. 单击您的“ dashboard” “
  3. 单击“新””
  4. 单击“创建新应用”
  • 给该应用程序一个名称,然后
  1. 在server.js文件中单击“创建应用”:
const path = require("path");
    
    //
    
    app.use(express.static(path.join(__dirname, "client", "build")));
    
    //
    
    app.get("*", (req, res) => {
        res.sendFile(path.join(__dirname, "client", "build", "index.html"));
    });
    
    // remember - in heroku app set a port like this - 
       const port = process.env.PORT || 5000;

  1. 在client package.json文件中添加“代理”:
"proxy": "http://localhost:8000" - or other url of your server

  1. 在Heroku App中设置环境变量:

转到“设置”
单击“揭示config vars”
添加一个新变量,然后单击“添加”。

  1. 在tokect.json中添加下一个脚本在root文件夹中:
"scripts": {
        "heroku-postbuild": "cd client && npm install --only=dev && npm install && npm 
         run build"
    }

  1. 在Procfile文件夹中:
 web: node server.js

  1. 在根文件夹中
    Heroku git:远程 - 一个app-name-from-heroku
    git init
    git添加。
    git commit -m“提交名称”
    您应用程序应该有效的Git推动Heroku Main(或主)

。我在Coursework.vschool.io(文章标题 - 将MERN应用程序部署到Heroku(使用您的git Master Branch& mongodb atlas)中找到了这个清晰的解决方案。

Thank you for all responses. I decided to put my whole app on heroku. It works. CORS policy problem disappeared. Problem was related to different domains (heroku for backend and netlify for frontend). I decided to use this solution because I have never tried to put frontend on heroku. It required a rearrangement of my project folder. Now my project structure is:

enter image description here

  1. Go to Heroku.com
  2. Click on your "Dashboard"
  3. Click "New"
  4. Click "Create New App"
  • Give the app a name and click "Create App"
  1. in server.js file add:

const path = require("path");
    
    //
    
    app.use(express.static(path.join(__dirname, "client", "build")));
    
    //
    
    app.get("*", (req, res) => {
        res.sendFile(path.join(__dirname, "client", "build", "index.html"));
    });
    
    // remember - in heroku app set a port like this - 
       const port = process.env.PORT || 5000;

  1. in client package.json file add "proxy":

"proxy": "http://localhost:8000" - or other url of your server

  1. set up enviroment variables in heroku app:

Go to "Settings"
Click "Reveal Config Vars"
Add a new variable and click "Add".

  1. add next script in package.json in root folder:

"scripts": {
        "heroku-postbuild": "cd client && npm install --only=dev && npm install && npm 
         run build"
    }

  1. in Procfile folder:

 web: node server.js

  1. in root folder
    heroku git:remote -a app-name-from-heroku
    git init
    git add .
    git commit -m "commit name"
    git push heroku main (or master)

You app should works. I found this clear solution in coursework.vschool.io (article title - Deploying MERN App to Heroku (using your Git Master Branch & MongoDB Atlas).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文