You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

部署到Heroku后后端无法运行的技术求助

问题描述
  • 应用部署到Heroku后后端无法正常运行,控制台出现axios错误
  • 本地运行相同代码无异常,仅能访问前端首页,无法进入其他页面
  • 已配置app.listen(process.env.PORT || 8000),调整过app.listen位置,也添加了MongoDB URL配置变量,问题仍未解决

后端主文件 index.js

const express = require('express')
const {MongoClient} = require('mongodb')
const {v4: uuidv4} = require('uuid')
const jwt = require('jsonwebtoken')
const cors = require('cors')
const bcrypt = require('bcrypt')
require('dotenv').config()


const uri = process.env.MONGODB_URL
const app = express()
app.use(cors())
app.use(express.json())
// const PORT = process.env.PORT || 8000



//----------------------------------- SIGNUP -----------------------------------
app.post('/signup', async (req, res) => {
    const client = new MongoClient(uri)
    const {email, password} = req.body

    const generateduserId = uuidv4()
    const hashedPassword = await bcrypt.hash(password, 10)  //learn more about hashed passwords

    try {
        await client.connect()
        const database = client.db('app-data')
        const users = database.collection('users')


        //check user exists 
        const existingUser = await users.findOne({email})
        if (existingUser) {
            return res.status(409).send('user already exists, please login')
        }

        const sanitizedEmail = email.toLowerCase()

        //configuring data before adding to database
        const data = {
            user_id: generateduserId,
            email: sanitizedEmail,
            hashed_password: hashedPassword
        }
        const insertedUser = await users.insertOne(data) //inserts user (data) to database


        //insertedUser and sanitizedEmail are required values to generate a token
        const token = jwt.sign(insertedUser, sanitizedEmail, {
            expiresIn: 60 * 24,  //expires in 24 hours
        })
        res.status(201).json({token, userId: generateduserId})

    } catch (error) {
        console.log(error)
    }
})

//----------------------------------- LOGIN -----------------------------------
app.post('/login', async (req, res) => {
    const client = new MongoClient(uri)
    const {email, password} = req.body

    try {
        await client.connect()
        const database = client.db('app-data')
        const users = database.collection('users')

        const user = await users.findOne({email})

        const correctPassword = await bcrypt.compare(password, user.hashed_password)

        if (user && correctPassword) {
            const token = jwt.sign(user, email, {
                expiresIn: 60 * 24,  //expires in 24 hours
            })
            res.status(201).json({token, userId: user.user_id})
        } else {
            res.status(400).send('invalid credentials')
        }
    } catch (error) {
        console.log(error)
    }
})


//----------------------------------- UPDATING -----------------------------------
app.put('/user', async (req, res) => {
    const client = new MongoClient(uri)
    const formData = req.body.formData

    try {
        await client.connect()
        const database = client.db('app-data')
        const users = database.collection('users')

        const query = {user_id: formData.user_id}
        const updateDocument = {
            $set: {
                first_name: formData.first_name,
                dob_day: formData.dob_day,
                dob_month: formData.dob_month,
                dob_year: formData.dob_year,
                show_gender: formData.show_gender,
                gender_identity: formData.gender_identity,
                gender_interest: formData.gender_interest,
                url: formData.url,
                about: formData.about,
                matches: formData.matches
            },
        }
        const insertedUser = await users.updateOne(query, updateDocument)
        res.send(insertedUser)
    } finally {
        await client.close()  //ensures client will close when finished or error occurs
    }
})


//----------------------------------- DASHBOARD -----------------------------------
app.get('/user', async (req, res) => {
    const client = new MongoClient(uri)
    const userId = req.query.userId

    try {
        await client.connect()
        const database = client.db('app-data')
        const users = database.collection('users')

        const query = {user_id: userId}
        const user = await users.findOne(query)
        res.send(user)


    } finally {
        await client.close()  
    }

}) 

//ONLY DISPLAY THE GENDER THAT USER WANTS 
app.get('/gendered-users', async (req, res) => {
    const client = new MongoClient(uri)
    const gender = req.query.gender


    try {
        await client.connect()
        const database = client.db('app-data')
        const users = database.collection('users')
        const query = { gender_identity: {$eq: gender} }
        const foundUsers = await users.find(query).toArray()  //return users with genders are interested in

        res.send(foundUsers)
    } finally {
        await client.close()  //ensures client will close when finished or error occurs
    }
})

//ADD MATCHES TO USER DATABASE
app.put('/addmatch', async (req, res) => {
    const client = new MongoClient(uri)
    const {userId, matchedUserId} = req.body


    try {
        await client.connect()
        const database = client.db('app-data')
        const users = database.collection('users')


        const query = {user_id: userId}
        const firstUpdateDocument = {
            $push: {matches: {user_id: matchedUserId}}
        }
        const firstMatchesUpload = await users.updateOne(query, firstUpdateDocument)

        const userMatches = await users.findOne({user_id: userId})


        //remove duplicates 
        const jsonObj = userMatches.matches.map(JSON.stringify)
        const uniqueSet = new Set(jsonObj)
        const uniqueArray = Array.from(uniqueSet).map(JSON.parse)


        const user = await users.updateOne(query, {$set: {matches: uniqueArray}})


        res.send(user)
    } finally {
        await client.close()
    }
})

//SHOW MATCHED USERS IN MATCHES DISPLAY
app.get('/users', async (req, res) => {
    const client = new MongoClient(uri)
    const userIds = JSON.parse(req.query.userIds)


    try {
        await client.connect()
        const database = client.db('app-data')
        const users = database.collection('users')

        const pipeline = [
            {
                '$match': {
                    'user_id': {
                        '$in': userIds
                    }
                }
            }
        ]
        const foundUsers = await users.aggregate(pipeline).toArray()

        res.send(foundUsers)

    } finally {
        await client.close()
    }
})


//----------------------------------- CHAT -----------------------------------
app.get('/messages', async (req, res) => {
    const client = new MongoClient(uri)
    const {userId, correspondingUserId} = req.query

    try {
        await client.connect()
        const database = client.db('app-data')
        const messages = database.collection('messages')
    
        const query = {
            from_userId: userId, to_userId: correspondingUserId
        }
        const foundMessages = await messages.find(query).toArray()
        res.send(foundMessages)
    } finally {
        await client.close() 
    }
})


app.post('/message', async (req, res) => {
    const client = new MongoClient(uri)
    const message = req.body.message

    try {
        await client.connect()
        const database = client.db('app-data')
        const messages = database.collection('messages')
        
        const insertedMessage = await messages.insertOne(message)
        res.send(insertedMessage)
    } finally {
        await client.close()
    }
})




app.use(express.static(__dirname + '/client/build'))

app.get('*', (req, res) => {
    res.sendFile(__dirname + '/client/build/index.html')
})

app.listen(process.env.PORT || 8000)

错误触发文件 AuthModal.js

import {useState} from 'react'
import axios from 'axios'  
import {useNavigate} from 'react-router-dom'
import {useCookies} from'react-cookie'

const AuthModal = ({setShowModal, isSignUp}) => {
    const [email, setEmail]  = useState(null)
    const [password, setPassword]  = useState(null)
    const [confirmPassword, setConfirmPassword]  = useState(null)
    const [error, setError]  = useState(null)
    const [cookies, setCookie, removeCookie] = useCookies(['user'])

    let navigate = useNavigate()

    isSignUp ? console.log(email, password, confirmPassword) : console.log(email, password)


    const handleClick = () => {
        setShowModal(false)
    }

    const handleSubmit = async (e) => {
        e.preventDefault() //prevents page from refreshing
        try {
            if (isSignUp && (password !== confirmPassword)) {
                setError('Passwords do not match')
                return
            }

            // posts the email and password to the signup in backend or to login depending
            const response = await axios.post(`http://localhost:8000/${isSignUp ? 'signup': 'login'}`, {email, password})
            
            console.log('response', response)
            //these are the parameters returned from the jsonwebtoken response status code
            setCookie('AuthToken', response.data.token)
            setCookie('UserId', response.data.userId)

            const success = response.status === 201
            if (success && isSignUp) navigate ('/onboarding') //if post request successful and sigin up, navigate to onboarding page
            if (success && !isSignUp) navigate ('/dashboard') //if post request successful and loggin in, navigate to dashboard page
        
            window.location.reload() //make sure auth token is read by /onboarding and /dashboard

        } catch (error) {
            console.log(error)
        }
    }

    return (
        <div className="auth_modal">
            <div className='close_icon' onClick={handleClick}>✕</div>
            <h2>{isSignUp ? 'CREATE ACCOUNT' : 'LOG IN'}</h2>
            <p>By Logging In you agree to our T&Cs</p>
            <form onSubmit={handleSubmit}>
                <input 
                    type='email' 
                    id='email' 
                    name='email' 
                    placeholder='email' 
                    required={true} 
                    onChange={(e) => setEmail(e.target.value)}
                />

                <input 
                    type='password' 
                    id='password' 
                    name='password' 
                    placeholder='password' 
                    required={true} 
                    onChange={(e) => setPassword(e.target.value)}
                />

                {isSignUp && <input 
                    type='password' 
                    id='password-check' 
                    name='password-check' 
                    placeholder='confirm password' 
                    required={true} 
                    onChange={(e) => setConfirmPassword(e.target.value)}
                /> }

                <input className='secondary_button' type='submit' />
                <p>{error}</p>

            </form>

            <hr/>
            <h2>GET THE APP</h2>

        </div>
    )
}

export default AuthModal

后端package.json

{
  "name": "server",
  "version": "1.0.0",
  "engines": {
    "node": "18.9.1"
  },
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "heroku-postbuild": "cd client && npm i && npm run build"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "bcrypt": "^5.1.0",
    "cors": "^2.8.5",
    "dotenv": "^16.0.3",
    "express": "^4.18.2",
    "jsonwebtoken": "^9.0.0",
    "mongodb": "^4.13.0",
    "nodemon": "^2.0.20",
    "uuid": "^9.0.0"
  }
  
}

排查建议

  • 前端请求地址问题:AuthModal.js中硬编码了http://localhost:8000作为请求地址,部署到Heroku后前端仍会向本地8000端口发请求,导致axios错误。应改为相对路径,比如/${isSignUp ? 'signup': 'login'},这样会自动使用当前页面的域名和端口发送请求。
  • 检查Heroku日志:通过heroku logs --tail命令查看详细错误日志,确认后端是否有连接MongoDB失败、端口监听异常等问题。
  • CORS配置优化:当前CORS配置为允许所有跨域请求,可尝试指定具体的前端域名,避免潜在的跨域问题。
  • MongoDB连接验证:确认Heroku配置的MONGODB_URL是否正确,可在本地使用该URL测试连接是否正常。
  • 端口监听位置:确保app.listen在所有路由定义之后,当前代码位置正确,但可尝试添加监听成功的日志:
    app.listen(process.env.PORT || 8000, () => {
      console.log(`Server running on port ${process.env.PORT || 8000}`)
    })
    
    便于确认服务是否正常启动。

内容的提问来源于stack exchange,提问作者Edris Elbow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 18:40:39