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/ .
我正在尝试在 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/ .