Node (Express) / React app only works locally - node.js

Issue Outline
I have a simple Node / React app, which runs fine using heroku local.
The Node backend has an /api/users endpoint, which returns some hardcoded JSON.
After heroku local, visiting localhost:5000/users works as expected, displaying the hardcoded users as per the Users.js React component.
However, when pushed to Heroku, only the / root page works. Going to /users shows Not Found, and going to /api/users returns the JSON.
I'm not sure why it works locally and not on Heroku, any help is appreciated.
Code
Express routes:
var express = require('express');
var router = express.Router();
var path = require('path');
router.get('/api/users', function(req, res, next) {
res.json([{id: 1, name: 'Colin'}, {id: 2, name: 'Spodie'}])
});
module.exports = router;
The Express app.js file:
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var cors = require('cors');
var config = require('./config/dev')
var index = require('./routes/index');
var app = express();
app.use(cors())
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
// serve static files from React
app.use(express.static(path.join(__dirname, '../frontend/build')));
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
React App.js file:
import React, { Component } from 'react';
import { Switch, Route } from 'react-router-dom';
import Users from './Users';
import NotFound from './NotFound';
import Home from './Home';
class App extends Component {
state = {users : []}
render() {
return (
<div>
<Switch>
<Route exact path='/' component={Home}/>
<Route exact path='/users' component={Users}/>
<Route component={NotFound} />
</Switch>
</div>
);
}
}
export default App;
React api.js file:
const api = process.env.BACKEND_URL || 'http://localhost:5000'
export const getUsers = () => (
fetch(`${api}/api/users`)
.then(res => res.json())
)
React package.json
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^15.6.1",
"react-dom": "^15.6.1",
"react-router-dom": "^4.2.2",
"react-scripts": "1.0.12"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"proxy": "http://localhost:5000"
}
Screenshots
Home page running locally
Home page running on Heroku
Users page running locally
Users page running on Heroku

You are serving static files but you also need to forward all HTTP GET requests to client side react router so it can handle. You may want to put this route to your app.js:
app.get("*", function (req, res) {
res.sendFile(__dirname + "/path/to/index.html")
})

Related

No "Access-Control-Allow-Oriign" header is present on the requested resource — localhost

I've tried what feels like every answer on Stack Overflow regarding CORS and React, but I can't seem to get anything to work. I want to run React on localhost:3000 and Express on localhost:9090, then post some data from React to Express, and get back a message that says the server got the data.
I always get this error:
Access to fetch at 'http://127.0.0.1:9090/api' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
In my server I have two files involved in this:
api.js
var express = require("express");
var router = express.Router();
router.get('/', function(req, res, next) {
res.send("API is working properly");
});
router.post('/', function(req, res, next) {
console.log(req.body);
res.setHeader('content-type', 'application/json');
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.header("Access-Control-Allow-Origin", "http://localhost:3000");
res.send("result gotten!");
});
module.exports = router;
app.js
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
// Require routes
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var apiRouter = require('./routes/api');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
// Tell express to use specific routes
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/api', apiRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
In my client I have SchoolForm.js
import React, {Component} from "react";
class SchoolForm extends Component {
/* https://reactjs.org/docs/forms.html */
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
console.log("Form was submitted with value " + this.state.value);
event.preventDefault();
const data = new FormData(this.current);
fetch('http://127.0.0.1:9090/api', {
method: 'POST',
body: data,
headers: {'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
"Access-Control-Allow-Methods": "DELETE, POST, GET, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With"},
})
.then(res => res.json())
.then(json => console.log(json))
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
School:
<input type="text" name="schoolName" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
export default SchoolForm;
I've also tried setting the proxy in my client's package.json:
{
"name": "maps-app",
"version": "0.1.0",
"private": true,
"proxy": "http://localhost:9090",
"dependencies": {
"#testing-library/jest-dom": "^5.11.9",
"#testing-library/react": "^11.2.5",
"#testing-library/user-event": "^12.8.1",
"leaflet": "^1.7.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-leaflet": "^3.1.0",
"react-scripts": "4.0.3",
"web-vitals": "^1.1.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
How do I get past this CORS block??
Install cors package
npm i --save cors
Then in your express app,
const app = express();
app.use(cors());
or else you can also add cors() to selected routes,
For example,
app.get('/', cors(), (req,res) => {})
Also you can pass cors options & configurations. To see available options please check the package - https://www.npmjs.com/package/cors
Note - If you need to add a proxy server for development process with create-react-app, try the manual approach.

HPM Error occurred while trying to proxy request in Heroku

I get this error: [HPM] Error occurred while trying to proxy request /api/artefact from myapp.herokuapp.com to http://localhost:5000 (ECONNREFUSED) when I deploy my MERN app.
These are the relevant files in terms of proxying and deploying:
setupProxy.js
const proxy = require("http-proxy-middleware");
module.exports = app => {
app.use(proxy("/api/", { target: "http://localhost:5000" }));
};
server.js
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const logger = require("morgan");
const { mongo_uri } = require("./config/config");
const path = require("path");
let cors = require("cors");
const PORT = process.env.PORT || 5000;
const app = express();
app.use(cors());
// Set up Routes
const artefactRoutes = require("./routes/api/artefact.js");
const userRoutes = require("./routes/api/users.js");
const authRoutes = require("./routes/api/auth.js");
// Connect to MongoDB Database
const dbRoute = mongo_uri;
mongoose
.connect(dbRoute, { useNewUrlParser: true })
.then(() => console.log("Connected to Database"))
.catch(err => console.log(err));
// Body Parser Middleware to parse request body into readable json format
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// Used for logging
app.use(logger("dev"));
app.use("/api", artefactRoutes);
app.use("/api/users", userRoutes);
app.use("/api/auth", authRoutes);
if (process.env.NODE_ENV === "production") {
app.use(express.static(path.join(__dirname, "../client/build")));
app.get("*", (req, res) => {
// relative path
res.sendFile(path.join(__dirname, "../client", "build", "index.html"));
});
}
app.listen(PORT, () =>
console.log(`Static server has started. Listening on port ${PORT}`)
);
static.json (though I'm not sure really what this does as I just found someone do this on another thread but it didn't change anything for me)
{
"root": "build/",
"clean_urls": false,
"routes": {
"/**": "index.html"
}
}
and my scripts in the parent directory package.json
"scripts": {
"start": "concurrently \"cd backend && node server.js\" \"cd client && npm start\"",
"format": "prettier",
"heroku-postbuild": "cd client && npm install && npm run build"
},
and it might help to mention that my folder structure is like:
-/project
--/client
--/backend
Basically the problem I'm having is that I've deployed my react app to heroku and the frontend loads fine, but none of the axios api calls to my backend are returning anything and just give me a 504 timeout error, and in the heroku logs it just says the proxy error. Any help would be much appreciated!

Deploying an Angular Build on Nginx for Windows

I'm trying to deploy my Angular Project's build (which also has a REST API in it) on nginx, but the localhost refuses to connect when I load the Dist inside the html folder.
A localhost run using npm run build does listen to it, but I cannon seem to crack the code on how to deploy that exact output onto the Nginx webserver.
(Inside my Angular Project)
server.js
// Get dependencies
const express = require('express');
const path = require('path');
const http = require('http');
const bodyParser = require('body-parser');
// Get our API routes
const api = require('./server/routes/api');
const app = express();
// Parsers for POST data
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// Point static path to dist
app.use(express.static(path.join(__dirname, 'dist')));
// Set our api routes
app.use('/api', api);
// Catch all other routes and return the index file
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist/ProjectName/index.html'));
});
/**
* Get port from environment and store in Express.
*/
const port = process.env.PORT || '3000';
app.set('port', port);
/**
* Create HTTP server.
*/
const server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port, () => console.log(`API running on localhost:${port}`));
server/routes/api (for testing)
const express = require('express');
const router = express.Router();
// declare axios for making http requests
const axios = require('axios');
const API = 'https://jsonplaceholder.typicode.com';
/* GET api listing. */
router.get('/', (req, res) => {
res.send('api works');
});
// Get all posts
router.get('/posts', (req, res) => {
// Get posts from the mock api
// This should ideally be replaced with a service that connects to MongoDB
axios.get(`${API}/posts`)
.then(posts => {
res.status(200).json(posts.data);
})
.catch(error => {
res.status(500).send(error)
});
});
module.exports = router;
package.json script
"scripts": {
"ng": "ng",
"start": "ng serve --proxy-config proxyConfig.json",
"build": "ng build && node server.js",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
}
proxyConfig.json
{
"/api": {
"target": "http://localhost:3000",
"secure": false,
"changeOrigin": true
}
}
nginx.conf
server {
listen 8080;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html/dist/ProjectName;
index index.html index.htm;
}
location /api {
proxy_pass http://localhost:3000;
Replace the line
app.use(express.static(path.join(__dirname, 'dist')));
with
app.use(express.static(path.join(__dirname, 'dist/ProjectName')));

Deploying MongoDB & Express based back-end API to Heroku dosen't work

I wrote a small front-end project with Vue.js, and Express was adopted in the back-end. The front-end and back-end were in two different directories respectively. The API works in my local environment but when I deploy my back-end api to Heroku, and tried GET request by Postman, it doesn't work.
In my index.js:
This is the listening port:
const port = process.env.PORT || 5000;
I am using mongoose to connect mLab:
const db = require("./config/keys").mongoURI;
mongoose.connect(db)
And my package.jsonis here:
{
"name": "backend-api",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"start": "node index.js",
"server": "nodemon index.js"
},
"author": "Yeoman_Li",
"license": "ISC",
"devDependencies": {
"body-parser": "^1.18.3",
"express": "^4.16.4",
"mongoose": "^5.4.0"
}
}
My index.js is here:
const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const app = express();
// CORS
app.all("*", (req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS");
res.header(
"Access-Control-Allow-Headers",
"Content-type, Accept, X-Access-Token, X-Key"
);
if ("OPTIONS" == req.method) res.status(200).end();
else next();
});
const mediaReq = require("./routes/api/mediaReq");
// DB
const db = require("./config/keys").mongoURI;
// body-parser
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// connect to DB
mongoose
.connect(db)
.then(() => console.log("MonogoDB Connected"))
.catch(err => console.log(err));
// router
app.use("/api/mediaReq", mediaReq);
// port
const port = process.env.PORT || 5000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
And here is my api router:
const express = require("express");
const router = express.Router();
const mediaController = require("../../controllers/media");
// test
router.get("/test", (req, res) => {
res.json({ msg: "test works" });
});
// $route GET api/mediaReq/medias || /medias/:id
router.get("/medias", mediaController.allMedia);
router.get("/medias/:id", mediaController.byId);
router.post("/medias", mediaController.create);
router.delete("/medias/:id", mediaController.remove);
module.exports = router;
I used postman to send a simple GET request and it works:
http://localhost:5000/api/mediaReq/medias
But when I tried to send a GET request to my heroku backend-api, it doesn't work:
https://fast-reaches-23458.herokuapp.com/api/mediaReq/medias

Deployment of Nodejs Code with Google Cloud

I am trying to deploy my webapp code built on NODE JS. I deployed a simple application hello-world from
https://github.com/GoogleCloudPlatform/nodejs-docs-samples/tree/master/appengine/hello-world
and it worked.
Attached is my project structure.
In the client folder i have angular4 where the build files will be stored in public
In the server folder i have defined routes.
This setup is working find on my local. No errors.
But when i deploy it on google cloud i am getting
Error: Server Error
The server encountered a temporary error and could not complete your request.
Please try again in 30 seconds.
and error log says
Error: No default engine was specified and no extension was provided.
at View (/app/server/node_modules/express/lib/view.js:61)
at EventEmitter.render (/app/server/node_modules/express/lib/application.js:570)
at ServerResponse.render (/app/server/node_modules/express/lib/response.js:1008)
at (/app/server/app.js:42)
at Layer.handle_error (/app/server/node_modules/express/lib/router/layer.js:71)
at trim_prefix (/app/server/node_modules/express/lib/router/index.js:315)
at (/app/server/node_modules/express/lib/router/index.js:284)
at Function.process_params (/app/server/node_modules/express/lib/router/index.js:335)
at next (/app/server/node_modules/express/lib/router/index.js:275)
at Layer.handle_error (/app/server/node_modules/express/lib/router/layer.js:67)
Can anyone guide me on why this error?
My root package.json file to run npm start is
{
"name": "test-application",
"description": "A simple application for testers who run",
"version": "0.0.1",
"private": true,
"license": "Apache-2.0",
"author": "Alaksandar Jesus Gene",
"repository": {
"type": "git",
"url": ""
},
"engines": {
"node": ">=4.3.2"
},
"scripts": {
"deploy": "gcloud app deploy",
"start": "node server/app.js"
},
"dependencies": {
},
"devDependencies": {
},
"cloud-repo-tools": {
"test": {
"app": {
"msg": "Hello, world!"
}
},
"requiresKeyFile": true,
"requiresProjectId": true
}
}
And my app.yaml file is
# [START app_yaml]
runtime: nodejs
env: flex
skip_files:
- ^client$
# [END app_yaml]
Point to note, both files were copied from google sample github project and added values.
Project Folder Structure - Expanded
Project Folder Structure - Collapsed
Can anyone guide me on why this error?
Fixed it. My mistake in app.js coding
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
const http = require('http');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var api = require('./routes/api');
var app = express();
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, '../public')));
app.use('/api', api);
app.get('*', (req, res) => { //i think the issue was here. I had app.use
res.sendFile(path.join(__dirname, '/index.html'));
});
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
const port = process.env.PORT || '3000';
app.set('port', port);
const server = http.createServer(app);
server.listen(port, () => console.log(`Running on localhost:${port}`));
module.exports = app;

Resources