PERN stack app on Heroku syntax error: Unexpected token < in JSON at position 0

PERN stack app on Heroku syntax error: Unexpected token < in JSON at position 0

我正在尝试在 Heroku 上部署我的第一个 React.js 应用程序。一切似乎都工作正常 link to app 除了最重要的部分 - 我用来从我的 Postgres 数据库和 Stripe API 获取数据的 Express.js REST API 在本地主机上正常运行,但是当我在 Heroku 上部署应用程序时,我尝试访问的所有 API 路由 return 相同的语法错误 - [=75 中的意外令牌 < =] 在位置 0.

我知道这个问题与我的应用程序如何路由到 API 有关。换句话说,获取请求无法到达所需的端点,因此 return 此语法错误,但我无法准确指出问题出在哪里 - 我是否在某处遗漏了“/”,是吗?错误地设置了我的环境变量等?

是否有人遇到过类似问题,或者有人可以在下面的代码中发现问题?

package.json

{
...
  "private": true,
  "main": "server.js",
  "homepage": "https://dj-bbq.herokuapp.com",
  "engines": {
    "npm": "6.14.15",
    "node": "14.18.1"
  },
  "dependencies": {
    "@formspree/react": "^2.2.4",
    "@stripe/react-stripe-js": "^1.7.0",
    "@stripe/stripe-js": "^1.22.0",
    "@testing-library/jest-dom": "^5.16.1",
    "@testing-library/react": "^12.1.2",
    "@testing-library/user-event": "^13.5.0",
    "@userfront/react": "^0.2.22",
    "cors": "^2.8.5",
    "express": "^4.17.2",
    "express-fileupload": "^1.2.1",
    "helmet": "^5.0.1",
    "jsonwebtoken": "^8.5.1",
    "pg": "^8.7.1",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-router-dom": "^6.2.1",
    "react-scripts": "4.0.3",
    "stripe": "^8.195.0",
    "web-vitals": "^2.1.2"
  },
  "devDependencies": {
    "dotenv": "^10.0.0",
    "nodemon": "^2.0.15",
    "source-map-explorer": "^2.5.2"
  },
  "scripts": {
    "start": "node server.js",
    "heroku-postbuild": "npm install && npm run build",
    "dev-start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "analyze": "source-map-explorer 'build/static/js/*.js'",
    "server": "node server.js",
    "nodemon": "nodemon server.js"
  },...

server.js

const express = require('express');
const helmet = require('helmet'); 
const cors = require('cors'); 
const path = require('path'); // Allows to access files through the server in our filesystem
const fileUpload = require('express-fileupload'); // Parses multipart/form-data requests, extracts the files if available, and make them available under req.files property.

/**
**  ------------- GENERAL SETUP -------------
*/

// Provides access to variables from the .env file by using process.env.REACT_APP_variable_name
    require('dotenv').config();

    const nodeEnv = process.env.NODE_ENV !== 'production';

    const devPort = process.env.REACT_APP_server_dev_port;
    const prodPort = process.env.PORT // process.env.PORT 

    const devDomain = process.env.REACT_APP_dev_domain;
    const prodDomain= process.env.REACT_APP_prod_domain;
    
    const PORT = nodeEnv ? devPort : prodPort;
    const domain = nodeEnv ? devDomain : prodDomain;

// CORS options
const corsOptions = {
    origin: domain, // frontend_URL for heroku deployment
    credentials: true ,
    // Allows only the following HTTP requests to go through
    methods: [
        "PUT", 
        "POST", 
        "DELETE", 
        "GET",
    ],
    "Access-Control-Allow-Headers": [
        "Origin", 
        "X-Requested-With", 
        "Content-Type", 
        "Accept", 
        "Authorization",
    ],
};

//* Creates the Express server instance as "app" 
    const app = express();

//* MIDDLEWARE

// Called BETWEEN processing the Request and sending the Response in your application method.
    app.use(helmet()); // Sets many http headers to make them more secure
    app.use(cors(corsOptions)); // To allow cross origin conections (Allows our React app to make HTTP requests to Express application)        
    
    // Instead of using body-parser middleware, use the new Express implementation of the same thing
        app.use(express.json()); // To recognize the incoming Request Object (req.body) as a JSON Object
        app.use(express.urlencoded({ extended: true })); // To recognize the incoming Request Object as strings or arrays
    app.use(fileUpload({
        createParentPath: true
    })); // Enables file uploading

//* HEROKU MIDDLEWARE

    if (process.env.NODE_ENV !== 'production') {
        // To load static files or client files from here http://localhost:3000/images/kitten.jpg
            app.use(express.static(path.join(__dirname, 'public')));

    } else if (process.env.NODE_ENV === 'production') {
        // Serve static files - makes the build folder accessible to app.
            app.use(express.static(path.joins(__dirname, 'build'))); 
            // app.use(express.static(path.resolve(__dirname, 'build'))); 
    }

/**
** -------------- SERVER ----------------
*/
       
// Determines the PORT and enables LISTENing for requests on the PORT (http://localhost:8000)
       
    app.listen(PORT, () => {
      console.debug(`Server is listening at http://localhost:${PORT}`);
    });
  
/**
** ------- ROUTES / ENDPOINTS ---------
*/

// Go to /test to make sure the basic API functioning is working properly
    app.get('/test', (req, res) => {
        res.status(200).send('The Basic API endpoints are working.')
    });

// Imports all of the routes from ./routes/index.js
    app.use(require('./app-server/routes/allRoutes'));

// If req comes from one of these domains (origins), then allow the request with CORS.
    app.use((req, res, next) => {
        const corsWhitelist = [
            domain,
        ];
        if (corsWhitelist.indexOf(req.headers.origin) !== -1) {
            res.header('Access-Control-Allow-Origin', req.headers.origin);
        }
        next();
    });

我已经为不同文件中的未知路由设置了捕获所有路由(路由器)

//* HEROKU - catch all for unrecognised routes

    if (process.env.NODE_ENV === 'production') {
        // Serve index.html file if it doesn't recognize the route
            router.get('*', (req, res, next) => { // or * instead of /
                res.sendFile(path.join(__dirname, 'build', 'index.html')); // resolve instead of join
            })
    }

这里是一个获取请求的例子

    const [recipes, setRecipes] = useState([]);
 
    useEffect(() => {
        let interval;

        const fetchData = async () => {
            try {
                // const url = 'http://localhost:8000/recipes/display';
                const url = `${customProxy}/recipes/display`;
                const response = await fetch(url);
                const json = await response.json();

                setRecipes(json);
                
            } catch(error) {
                // console.error(error);
                alert("Recipe displaying:" + error);
            }
        };

        fetchData();

        interval = setInterval(() => {
            fetchData()
        }, 86 * 1000)
        return () => {
            clearInterval(interval)
        }

    }, []); // Determine swhen to re-use useEffect, if this changes.

提前感谢您花时间考虑问题的解决方案!

更新 1

我第 n 次开始研究我的项目,之前我按照 Heroku 上的指南部署我的 PERN 应用程序。指南建议使用 mars/create-react-app-buildpack 来部署应用程序,但在阅读此构建包的文档后,它清楚地表明此构建包仅适用于静态反应应用程序,而不是使用自己的自定义 node.js 服务器反应应用程序.

在这种情况下,我将使用mars/heroku-cra-node

我一直在关注有关如何设置文件夹结构等的文档,但现在,当我部署应用程序时,Heroku 会通知我以下内容...

-----> Building on the Heroku-20 stack
-----> Using buildpacks:
       1. https://github.com/mars/heroku-cra-node
-----> App not compatible with buildpack: https://github.com/mars/heroku-cra-node
       bash: /tmp/codon/tmp/buildpacks/d07ae047a3685d9cfb39224105301a7dbdbfbe9c/bin/detect: No such file or directory
       More info: https://devcenter.heroku.com/articles/buildpacks#detection-failure
 !     Push failed

我明白我的想法是我的文件夹结构不符合构建包的要求,但我严格遵守其文档。

有没有人有过使用这个构建包将 PERN 应用程序部署到 Heroku 的经验?

我注意到我的这个问题仍然没有答案。

最后的问题是,我尝试使用 Heroku 免费计划,但我的应用程序太“大”了,所以我要么需要拆分 back-end 和 front-end 分为两个应用程序或使用付费计划。

最后,我实际上将我的托管服务提供商从 Heroku 更改为 Digital Ocean。该应用程序仍在他们的服务器上并且现在可以使用 - https://dj-bbq-i5gdc.ondigitalocean.app/ .