In Development mode, react and express get connected perfectly and I am able to fetch data from backend. But, when using them in production mode, I am getting a status of 200 but unable to get the json response from express.
I am getting this error:
SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
Backend File is this:
const express = require('express');
const bodyParser = require("body-parser");
const cors = require("cors");
const path = require("path");
const helmet = require("helmet");
const compression = require("compression");
if(process.env.NODE_ENV !== "production") require('dotenv').config();
const app = express();
//middlewares
app.use(helmet());
app.use(compression());
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// serve the react app files in production
if(process.env.NODE_ENV === "production") {
app.use(express.static(path.join(__dirname, "client/build")));
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname, "client/build", "index.html"))
})
}
//port
const port = process.env.PORT || 5000;
app.listen(port, error => {
if(error) throw error;
console.log(`Backend is running on localhost:${port}`);
});
// connection route
app.get("/express", (req, res) => res.json({express: "YOUR EXPRESS BACKEND IS CONNECTED"}));
Backend package.json:
{
"name": "",
"version": "1.0.0",
"description": "",
"main": "server.js",
"engines": {
"node": "12.19.0",
"npm": "6.14.8"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "nodemon server.js",
"mainstart": "concurrently --kill-others-on-fail \"npm start\" \"cd client && yarn start\""
},
"repository": {
"type": "git",
"url": "git+https://github.com/g4rry420/armourLine.git"
},
"author": "Gurkiran Singh",
"license": "ISC",
"bugs": {
"url": ""
},
"homepage": "",
"dependencies": {
"body-parser": "^1.19.0",
"compression": "^1.7.4",
"concurrently": "^5.3.0",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"helmet": "^4.2.0",
"nodemon": "^2.0.6",
"path": "^0.12.7"
}
}
Front end App.js
import React,{ useEffect, useState } from 'react';
import { Route, Switch } from "react-router-dom"
import './App.css';
import Header from "./components/header/header.component"
import ProductItem from './components/product-item/product-item.component';
import Homepage from './pages/homepage.component';
import ProductForm from "./components/product-form/product-form.component";
function App() {
const [backendData, setBackendData] = useState("No Fetching")
useEffect(() => {
callBackedApi()
.then(res => setBackendData(res.express))
.catch(err => console.log(err))
}, [])
async function callBackedApi(){
const response = await fetch("/express", {
headers: {
'Content-Type': 'application/json',
},
})
const body = await response.json();
if(response.status !== 200){
throw Error(body.message)
}
return body;
}
return (
<div className="App">
{backendData}
<Header />
<div>
<Switch>
<Route exact path="/" component={Homepage} />
<Route path="/product" component={ProductItem} />
<Route path="/product-form" component={ProductForm} />
</Switch>
</div>
</div>
);
}
export default App;
Frontend package.json
{
"name": "armourline",
"version": "0.1.0",
"private": true,
"proxy": "http://localhost:5000",
"dependencies": {
"#testing-library/jest-dom": "^4.2.4",
"#testing-library/react": "^9.3.2",
"#testing-library/user-event": "^7.1.2",
"bootstrap": "^4.5.2",
"react": "^16.13.1",
"react-bootstrap": "^1.3.0",
"react-dom": "^16.13.1",
"react-icons": "^3.11.0",
"react-router-dom": "^5.2.0",
"react-scripts": "3.4.3",
"react-slick": "^0.27.13"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
You have a route ordering problem.
In production, you have this code active:
// serve the react app files in production
if(process.env.NODE_ENV === "production") {
app.use(express.static(path.join(__dirname, "client/build")));
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname, "client/build", "index.html"))
})
}
Which puts the app.get("*", ...) route BEFORE your app.get("/express", ...) route so the * route will grab the request and send it HTML which won't parse as JSON. The order of your routes matters when they can conflict.
Move your app.get("/express", ...) route BEFORE your app.get("*", ...) route, In fact, the * route should probably be last so all individually specified routes get a chance to grab the request before the * route does. Personally I don't use * routes myself because of some of the issues they can cause.
Related
I want to make a NextJS executable app for Windows, but I get this annoying error:
(node:18500) UnhandledPromiseRejectionWarning: Error: > Couldn't find a pages directory. Please create one under the project root
I have a pages folder in the root folder
Below is server.js
const { createServer } = require('http')
const next = require('next')
const port = parseInt(process.env.PORT, 10) || 3003
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev, dir: __dirname })
const handle = app.getRequestHandler()
app.prepare().then(() => {
createServer((req, res) => {
const parsedUrl = new URL(req.url, 'http://w.w')
const { pathname, query } = parsedUrl
if (pathname === '/a') {
app.render(req, res, '/a', query)
} else if (pathname === '/b') {
app.render(req, res, '/b', query)
} else {
handle(req, res, parsedUrl)
}
}).listen(port, (err) => {
if (err) throw err
console.log(`> Ready on http://localhost:${port}`)
})
})
And package.json
{
"name": "custom-server",
"version": "1.0.0",
"scripts": {
"dev": "node server.js",
"build": "next build",
"start": "cross-env NODE_ENV=production node server.js",
"pkg": "pkg . --targets node12-win-x64 --out-path pkg"
},
"dependencies": {
"cross-env": "^7.0.2",
"next": "latest",
"pkg": "^4.4.9",
"react": "^17.0.1",
"react-dom": "^17.0.1"
},
"main": "server.js",
"license": "MIT",
"bin": "server.js",
"pkg": {
"assets": [
".next/**/*"
],
"scripts": [
".next/server/**/*.js"
]
}
}
How can I solve this issue and make a NextJS working executable ?
When you run the build make sure you set NODE_ENV to production, also pass a conf to your server.js as per the docs.
const app = next({ dev, dir: __dirname, conf : {output : "standalone"} })
NODE_ENV should be production for all your tasks besides run dev.
Heroku is ignoring my .env file for some reason, even though express shows what port to use and etc. I'm getting this error
Web process failed to bind to $PORT within 60 seconds
I read other solutions that talk about removing dotenv but if i do that, it will crash my app. I would need dotenv to get env variables, is there way i can set an .env for heroku or what would be the best solution to deploy a express + react + postgress application on heroku ?
here is my Profile
web: node app.js
app.js
var express = require('express');
var app = express();
var userRoute = require('./routes/users');
var postRoute = require('./routes/posts');
var bodyParser = require('body-parser');
var logger = require('morgan');
var session = require('express-session');
var cookieParser = require('cookie-parser') ;
var dotenv = require('dotenv');
var env = dotenv.config();
var cors = require('cors');
var models = require('./models/');
const PORT = process.env.PORT || 8000;
const passport = require('passport');
const path = require('path');
// const allowOrigin = process.env.ALLOW_ORIGIN || '*'
// CORS Middleware
if (!process.env.PORT) {
require('dotenv').config()
}
// console.log(process.env.DATABASE_URL);
if (!process.env.PORT) {
console.log('[api][port] 8000 set as default')
console.log('[api][header] Access-Control-Allow-Origin: * set as default')
} else {
console.log('[api][node] Loaded ENV vars from .env file')
console.log(`[api][port] ${process.env.PORT}`)
console.log(`[api][header] Access-Control-Allow-Origin: ${process.env.ALLOW_ORIGIN}`)
}
require('./config/passport-github');
require('./config/passport');
app.use(logger('dev'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'build')));
app.use(cookieParser());
app.use(session({
secret : process.env.JWT_SECRET,
saveUninitialized: false,
maxAge: 1000 * 60 * 60 * 84,
resave: false
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended:false}));
const isAuthenticated = function(req, res, next){
if(req.isAuthenticated()){
next();
console.log('this works');
}else{
res.redirect('http://127.0.0.1:8001/signIn');
}
}
// app.use(function(req, res, next) {
// res.header('Access-Control-Allow-Origin', '*');
// // res.header('Access-Control-Allow-Credentials', true);
// res.header("preflightContinue", false)
// // res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
// res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
// next();
// });
app.use(cors({
'allowedHeaders': ['Content-Type'], // headers that React is sending to the API
'exposedHeaders': ['Content-Type'], // headers that you are sending back to React
'origin': '*',
'methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
'preflightContinue': false
}));
app.use('/api/users', userRoute );
app.use('/api/posts', isAuthenticated, postRoute );
app.use(function(req, res, next) {
res.locals.user = req.user; // This is the important line
// req.session.user = user
console.log(res.locals.user);
next();
});
models.sequelize.sync().then(() => {
const server = app.listen(PORT, () => {
console.log(`Server is up and running on port ${PORT}`);
});
});
package.json
{
"name": "sequelize-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"client": "cd ./client && npm start ",
"server": "nodemon app.js",
"start": "concurrently --kill-others \"npm run client\" \"npm run server\" ",
"heroku-postbuild": "cd client && npm install && npm run build"
},
"author": "",
"license": "ISC",
"dependencies": {
"async": "^2.6.1",
"bcrypt": "^3.0.2",
"body-parser": "^1.18.3",
"concurrently": "^4.1.0",
"cookie-parser": "^1.4.3",
"cookie-session": "^2.0.0-beta.3",
"cors": "^2.8.5",
"crypto": "^1.0.1",
"dotenv": "^7.0.0",
"express": "^4.16.4",
"express-flash": "0.0.2",
"express-session": "^1.15.6",
"foreman": "^3.0.1",
"jsonwebtoken": "^8.4.0",
"morgan": "^1.9.1",
"nodemailer": "^5.1.1",
"nodemon": "^1.18.9",
"passport": "^0.4.0",
"passport-github": "^1.1.0",
"passport-github2": "^0.1.11",
"passport-jwt": "^4.0.0",
"passport-local": "^1.0.0",
"pg": "^7.8.0",
"pg-hstore": "^2.3.2",
"sequelize": "^4.42.0"
},
"devDependencies": {
"chai": "^4.2.0",
"mocha": "^6.1.2"
}
}
config/database.js
if (!process.env.PG_DB) {
const fs = require('fs')
const dotenv = require('dotenv')
const envConfig = dotenv.config({silent: true})
// for (var k in envConfig) {
// process.env[k] = envConfig[k]
// }
console.log('[api][sequelize] Loaded database ENV vars from .env file')
}
module.exports = {
development: {
username: process.env.POSTGRES_USER,
password: process.env.POSTGRES_PASSWORD,
database: process.env.POSTGRES_DB,
host: process.env.POSTGRES_HOST,
dialect: 'postgres',
migrationStorageTableName: 'sequelize_meta'
},
production: {
username: "root",
password: null,
database: "*******",
host: "127.0.0.1",
dialect: "postgres"
}
}
config/config.json
{
"development": {
"username": "eli",
"password": "",
"database": "elitest4",
"host": "127.0.0.1",
"dialect": "postgres"
},
"test": {
"username": "root",
"password": null,
"database": "database_test",
"host": "127.0.0.1",
"dialect": "postgres"
},
"production": {
"username": "root",
"password": null,
"database": "*******",
"host": "127.0.0.1",
"dialect": "postgres"
}
}
client/package.json
{
"name": "client",
"version": "0.1.0",
"private": true,
"dependencies": {
"#material-ui/core": "^3.9.1",
"#material-ui/icons": "^3.0.2",
"axios": "^0.18.0",
"history": "^4.7.2",
"http-proxy-middleware": "^0.19.1",
"jsonwebtoken": "^8.4.0",
"jwt-decode": "^2.2.0",
"material-ui-icons": "^1.0.0-beta.36",
"moment": "^2.24.0",
"react": "^16.7.0",
"react-dom": "^16.7.0",
"react-redux": "^6.0.0",
"react-router-dom": "^4.3.1",
"react-scripts": "2.1.3",
"redux": "^4.0.1",
"redux-thunk": "^2.3.0",
"superagent": "^4.1.0"
},
"scripts": {
"start": "PORT=8001 react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"postinstall": "react-scripts build"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
],
"devDependencies": {
"dotenv": "^6.2.0"
}
}
This is a simple oversight when deploying to Heroku. Heroku manages its own env vars to keep them safe. You should not be committing your .env to git or any version control. In Heorkus case simply go to the apps dashboard, in the settings tab and click reveal config vars and put the content of your dot env in the ui.
You can use the link bellow put your app name in there and you should see the settings screen.
https://dashboard.heroku.com/apps/<your_app_name>/settings
Trying to work with reactjs (create-react-app) and now including expressjs. What I've done is
move my folder/* to folder/client/* (deleting node_modules)
cd folder/client/ and npm install to recreate the node_modules
*it works as before, the app renders well
cd folder and npm init
npm install express --save
write the folder/server.js
add the proxy settings in /folder/client/package.json
npm run start in /folder and in /folder/client
Then, I go to localhost:3000 and I get the reactjs app, without express anywhere. Then I go to localhost:8080 and I get the express result, which is indeed the same page as before but without being executed by react (nothing wrong here, I assume)
And then I go to localhost:3000/test and it gets proxied to express, where I see in the terminal the console.log of server.js
So I cannot proxy localhost:3000, but I can localhost:3000/whatever. What is wrong?
server.js
const express = require('express');
const path = require('path'); // haven't installed, should I?
const app = express();
app.use(express.static(path.join(__dirname, 'build'))); // of no use here
app.get('/ping', function (req, res) { // this one works
return res.send('pong');
});
// app.get('', function (req, res) { // doesn't work
// app.get('*', function (req, res) { // doesn't work
// app.get('.', function (req, res) { // doesn't work
// app.get('.*', function (req, res) { // doesn't work
// app.get('./', function (req, res) { // doesn't work
app.get('./*', function (req, res) { // doesn't work
console.log('hey') // never seen
res.sendFile(path.join(__dirname, 'client/src', 'index.html'));
});
app.get('/test', function (req, res) { // this one works
console.log('hey2') // I do see this when calling localhost:3000/test
res.sendFile(path.join(__dirname, 'client/src', 'index.html'));
});
app.listen(process.env.PORT || 8080);
package.json (/)
{
"name": "ouyea",
"version": "0.1.1",
"description": "This project was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app).",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js"
},
"repository": {
"type": "git",
"url": "git+https://xxxx"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://xxxx"
},
"homepage": "https://xxxx",
"dependencies": {
"express": "^4.16.4"
}
}
package.json (/client)
{
"name": "client",
"version": "0.1.1",
"private": true,
"dependencies": {
"axios": "^0.18.0",
"googleapis": "^33.0.0",
"papaparse": "4.6.0",
"react": "^16.4.2",
"react-dom": "^16.4.2",
"react-scripts": "1.1.4",
"semantic-ui-css": "^2.4.0",
"semantic-ui-react": "^0.82.5"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"proxy": {
"": { // I know comments don't work, but I put them here for clarity, none of them worked
// "*": {
// ".": {
// "/": {
"target": "http://localhost:8080"
},
"/test": {
"target": "http://localhost:8080"
}
}
}
The purpose of the express server is to simply a) render the base HTML page from your dist folder, and b) supply data from endpoints that you set up as routes in Express which can be accessed by your React client application. There are apps (universal) that can render React pages from Express but that's not what you're doing here with create-react-app.
So I got things working where it babel compiles everything, including next.js
I followed next.js docs on how to handle babel and created a .babelrc file:
{
"presets": ["next/babel", "es2015", "stage-0"]
}
When I run yarn run dev everything compiles and the server starts. When I load a page, next.js will run it's build process. Since things in the directory change, nodemon restarts the server and causes an infinite loop. Can some help me with this please???
This is my package.json file:
{
"name": "creatorsneverdie",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"dev": "nodemon app.js --exec babel-node --ignore node_modules, .next, yarn.lock",
"build": "next build",
"start": "NODE_ENV=production node app.js"
},
"dependencies": {
"axios": "^0.16.2",
"bcrypt": "^1.0.2",
"body-parser": "^1.17.2",
"cors": "^2.8.4",
"cryptr": "^2.0.0",
"dotenv": "^4.0.0",
"express": "^4.15.3",
"express-session": "^1.15.4",
"lodash": "^4.17.4",
"lowdb": "^0.16.2",
"next": "^2.4.7",
"passport": "^0.3.2",
"passport-jwt": "^2.2.1",
"react": "^15.6.1",
"react-dom": "^15.6.1"
},
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-loader": "^7.1.1",
"babel-preset-es2015": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"nodemon": "^1.11.0"
}
}
And the app.js file:
import express from 'express'
import session from 'express-session'
import bodyParser from 'body-parser'
import cors from 'cors'
import passport from 'passport'
const db = require('./db/index.js').initDb()
const writeSeeds = require('./db/index.js').writeSeeds
const routes = require('./routes/index')
require('dotenv').config({path: 'variables.env'});
// Next config
const next = require('next')
const dev = process.env.NODE_ENV !== 'production'
const nextLoader = next({dev})
const handle = nextLoader.getRequestHandler()
nextLoader.prepare().then(() => {
const app = express();
app.use(cors());
app.set('db', db);
app.nextRender = nextLoader
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(session({
secret: process.env.SECRET,
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
require('./config/passport')(passport);
app.use('/', routes)
app.get('*', (req, res) => {
return handle(req, res)
})
// START APP
app.set('port', process.env.PORT || 1337);
if(!db.has('products').value()) {
writeSeeds(db);
}
const server = app.listen(app.get('port'), () => {
console.log(`Express running -> ${server.address().port}`)
});
})
I was having the same problem and I fixed that by adding "pages/"* to nodemon's ignore list. Somehow the build process is doing the pages source rewrite without changing anything which is then causing nodemon to restart and triggers the rebuild again.
Try to change your dev script to:
nodemon -w app.js --exec babel-node
I built a small app on top of a company's API. I created my own API to interact with their's. My app uses React-Create-App to scaffold, with Redux, and the server is Experss/Node.js. It works totally fine on localhost but when I deploy it on Heroku, it serves the bundle but doesn't hand any user http requests to the server. It returns a 404 not found. Heroku gives no errors in their logs.
My post install script runs the build, which bundles the app. I have set environment port variable in the heroku dashboard as well.
package.json
{
"name": "event.me",
"version": "0.1.0",
"private": true,
"engines":{
"node": "7.2.1",
"npm": "3.10.10"
},
"dependencies": {
"axios": "^0.15.3",
"body-parser": "^1.17.1",
"express": "^4.15.2",
"moment": "^2.18.1",
"react": "^15.4.2",
"react-dom": "^15.4.2",
"react-progressbar.js": "^0.2.0",
"react-redux": "^5.0.3",
"react-router": "^3.0.0",
"react-router-dom": "^4.0.0",
"redux": "^3.6.0",
"redux-logger": "^3.0.0",
"redux-thunk": "^2.2.0"
},
"devDependencies": {
"eslint": "^3.18.0",
"eslint-config-airbnb": "^14.1.0",
"eslint-plugin-jsx-a11y": "^4.0.0",
"eslint-plugin-react": "^6.9.0",
"eslint-plugin-import": "^2.2.0",
"nodemon": "^1.11.0",
"react-scripts": "0.9.5"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"server": "node server/server.js",
"postinstall": "npm run build"
}
}
server.js file
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const routes = require('./routes');
const app = express();
app.use(express.static(path.resolve(__dirname, '..', 'build')));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use('/api', routes);
const port = process.env.PORT || 8080;
app.listen(port, () => console.log(`Listening on ${port}`));
routes.js
const routes = require('express').Router();
const users = require('./controllers/users.js');
const events = require('./controllers/events.js');
routes.post('/sigin', users.signin);
routes.post('/signup', users.signup);
routes.get('/events', events.getAll);
routes.post('/events', events.attendEvent);
routes.delete('/events', events.flakeEvent);
routes.patch('/events', events.updateEvent);
routes.delete('/singleevent', events.deleteEvent);
routes.post('/createevent', events.createEvent);
module.exports = routes;
If you are using Create React App, one of the things you must do if you want Webpack to be used as proxy when calling your API is to set in your package.json:
...
"proxy": "http://localhost:3030",
...
Thus you use Webpack included in Create React App to proxy all the calls to your API and do not need an extra server.
For Heroku deployment you need to change localhost for Heroku URL and 3030 for your established port in Express server (process.env.PORT).
That's where I think the problem lies.
Also, you have to route your non API petitions to your index.html.
Add this to your server.js.
app.get('*', (req, res) => {
const way = path.join(__dirname, 'build', 'index.html');
res.sendFile(way);
});