My react app is up on localhost:3000 and node server is running on localhost:5000.
When I am trying to connect to the express API the route is going to 'localhost:3000/auth/google' instead of localhost:5000/auth/google
UserAction.js
export const updateLoginUser = (userData, scheme) => async dispatch => {
console.log(scheme);
if(scheme === 'google') {
// TODO: fetch user from the DB
const fetchedUser = await axios.post('/auth/google');
dispatch({
type: UPDATE_USER,
payload: fetchedUser.data
})
} else {
// TODO: fetch user from the DB
const fetchedUser = await axios.post('/api/get_user/', {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify(userData)
})
dispatch({
type: UPDATE_USER,
payload: fetchedUser.data
})
}
}
setupProxy.js
const proxy = require('http-proxy-middleware')
module.exports = function(app) {
app.use(proxy('/auth/google', { target: 'http://localhost:5000' }))
}
NODE server.js
const express = require('express');
const mongoose = require('mongoose');
const keys = require('./config/keys');
const cookieSession = require('cookie-session');
const passport = require('passport');
const cors = require('cors');
const morgan = require('morgan');
require('./models/Users');
require('./services/passport'); // it is not returing anything hence no need of assigning
mongoose.connect(keys.mongoDBURI, { useNewUrlParser: true, useUnifiedTopology: true });
const app = express();
app.use(cors());
// Setup the cookie session
app.use(cookieSession({
maxAge: 30 * 24 * 60 * 1000, // Time till the cookie will be alive
keys: [keys.cookieKey]
}));
app.use(morgan('combined'));
// Make passport know to use the cookieSession
app.use(passport.initialize());
app.use(passport.session());
require('./routes/authRoutes')(app); // authRoute returing a function and we are immediatly invoking that function
const PORT = process.env.PORT || 5000;
app.listen(5000);
EDIT: react package.json
{
"name": "blogpost-frontend",
"version": "0.1.0",
"private": true,
"dependencies": {
"axios": "^0.19.0",
"http-proxy-middleware": "^0.20.0",
"node-sass": "^4.13.0",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-redux": "^7.1.3",
"react-router-dom": "^5.1.2",
"react-scripts": "3.2.0",
"redux": "^4.0.4",
"redux-thunk": "^2.3.0"
},
"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"
]
},
"proxy": "http://localhost:5000"
}
I am new to this hence I do not know how exactly proxy works.
you may need to rewrite your setupProxy.js as below
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/api',
createProxyMiddleware({
target: 'http://localhost:5000',
changeOrigin: true,
})
);
};
then do a npm install.
In my case that resolved the issue.
here you can see more details https://create-react-app.dev/docs/proxying-api-requests-in-development/
Also there it is mentioned as Note: This file only supports Node's JavaScript syntax. Be sure to only use supported language features (i.e. no support for Flow, ES Modules, etc).
The way to do it is to use what route you use in express at app.get('/what-here', ....) You use also in setupProxy.js at
function (app){ app.use('/what-here',
createproxyMiddleware({
target: URL,
change origin:true}).
The what-here is attached to the URL and the URL is proxied to the frontend URL that the client app uses. That is if the client is at localhost:3000 ant the server is at localhost:5000 the request is then at localhost:5000/what-here but it is shown as localhost:3000/what-here to the client. The mistake is to use different names at express then at setupProxy
I had the same problem. The problem is not the setupProxy.js, the problem is that the path to which axios is posting does not exist in the backend. This can be caused by a mispelling in the backend route.
Ensure the backend route path is the same as the frontend post path.
First ensure your setupProxy.js looks like this:
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/api',
createProxyMiddleware({
target: 'http://localhost:5000',
changeOrigin: true,
})
);
};
and the axios.post starts with /api,mine was:
await axios.post("/api/conversion/convert", data);
I was posting using axios.post('/api/conversion/convert') but my express backend had a post route 'api/convertion/convert' which was a typo on my part (convertion instead of conversion) which made axios post to 3000 instead of 5000.
I don't know what kind of a bug is this, axios posting to 3000 instead of 5000 for mismatch in the backend route. The expected behavior is that it should post to 5000/wrong-post-path for easier debugging.
P.S Please upvote if this solves your problem. I am new to answering question and the upvote would really boost my account.
i think you forgot to add first part of app.listen(api, proxy()) and you have only proxy method, am problem may be that you didn't specified which url/path to go through proxy.
var express = require('express');
var proxy = require('http-proxy-middleware');
var app = express();
app.use(
'/api', // you miss this part in your server code
proxy({ target: 'http://www.example.org', changeOrigin: true })
);
app.listen(3000);
// http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar
Related
I am trying to deploy my React app to heroku 22 stack, I have completed the code part of it and local machine it works fine. I have a Reactjs FrontEnd and Nodejs Backend. First After completing the project I moved my Reactjs frontend into the Nodejs Backend and run the npm run build command on the front end My folder structure looks like this:
but When I push my code to heroku using CLI
git push heroku master:main
I keep getting cannot get / or no such file or directory /app/vrestaurant/build/index.html
here is my package.json in backend
{
"name": "nodejs-mongodb-mongoose",
"version": "1.0.0",
"engines": {
"node": "17.x",
"npm": "8.x"
},
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node app.js"
},
"author": "Vishal",
"license": "ISC",
"dependencies": {
"body-parser": "^1.19.1",
"connect-mongo": "^4.6.0",
"connect-mongodb-session": "^3.1.1",
"cors": "^2.8.5",
"crypto": "^1.0.1",
"debug": "^4.3.4",
"dotenv": "^16.0.1",
"express": "^4.17.2",
"express-session": "^1.17.2",
"mini-css-extract-plugin": "^2.4.6",
"mongoose": "^6.1.7",
"nodemon": "^2.0.19",
"passport": "^0.5.2",
"passport-local": "^1.0.0",
"passport-local-mongoose": "^7.0.0",
"razorpay": "^2.8.1",
"shortid": "^2.2.16"
}
}
my app.js file on backend
// imports
require('dotenv').config();
const express = require('express')
const session = require("express-session")
const bodyParser = require('body-parser')
const cors = require('cors')
const zomatoRoutes = require('./Routes/zomato')
const paymentRoutes = require('./Routes/payments')
const mongoose = require('mongoose')
const passport = require("passport")
const LocalStrategy = require("passport-local")
const passportLocalMongoose = require("passport-local-mongoose")
const MongoStore = require('connect-mongo');
const MongoClient = require("mongodb").MongoClient;
const User = require('./Models/user')
// create express server
var app = express()
// add middleware before routes
app.use(bodyParser.json())
app.use(cors())
//connect to mongoDB
const uri = 'mongodb+srv://vishal_torne_22:*****#db-hernode.zu6btrs.mongodb.net/<mydbname>?retryWrites=true&w=majority';
console.log(uri, "this is the uri")
const options = {
useNewUrlParser: true,
useUnifiedTopology: true,
dbName:'zomato1'
}
mongoose.connect(uri, options).then(() => {
console.log('mongoose connected')
}).catch(e => console.log(e))
mongoose.connection.on('connected', () => {
console.log('mongoose is connected')
})
app.use(session({
secret: "this is unambigous secret",
resave: false,
saveUninitialized: false,
cookie: { maxAge: 24*60*60*1000 },
store : MongoStore.create({
mongoUrl: uri,
ttl: 14 * 24 * 60 * 60
})
}));
app.use(passport.initialize());
app.use(passport.session());
// middleware routes
app.use('/zomato', zomatoRoutes)
app.use('/payment', paymentRoutes)
// heroku configuration
if(process.env.NODE_ENV=="production"){
app.use(express.static('vrestaurant/build'))
const path = require('path')
app.get("*", (req, res) => {
res.sendFile(path.resolve(__dirname, "vrestaurant/build/index.html"))
})
}
//listen to a port
app.listen( process.env.PORT ||5252 , () => {
console.log("express app is up and running on port 5252");
console.log(process.env.PORT,'this is the env port')
})
things I have tried :
I have added heroku post build but gives error ' Missing Build script'
I added build webpack in package.json but it gives me error
also added the https://github.com/mars/create-react-app-buildpack.git build pack using heroku website settings, It gives me error :
Downloading Buildpack: https://github.com/mars/create-react-app-inner-buildpack.git#v9.0.0
remote: =====> Detected Framework: React.js (create-react-app)
remote: Writing `static.json` to support create-react-app
remote: Enabling runtime environment variables
remote: =====> Downloading Buildpack: https://github.com/heroku/heroku-buildpack-static.git#21c1f5175186b70cf247384fd0bf922504b419be
remote: =====> Detected Framework: Static HTML
remote: Stack heroku-22 is not supported!
remote: ! Push rejected, failed to compile React.js (create-react-app) multi app.
can anybody help me out with this please? Thank you.
Your buildpack should be Node.Js and not react.
Also try to print the output of
path.resolve(__dirname, "vrestaurant/build/index.html") to verify if it is resolving to correct path for index.html file in build folder.
Provide full path in setting the static folder
app.use(express.static(path.resolve(__dirname, "vrestaurant/build/index.html")))
Actually it might be better for you to use os.path.join() because it can cause on error on different systems:
you can use:
const path = require('path');
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'vrestaurant', 'build', 'index.html'));
});
I am trying to deploy my first React.js app on Heroku. Everything seems to work fine link to app except the most important part - the Express.js REST API that I use to fetch data from my Postgres database and Stripe API is functioning normally on localhost, but when I deploy the app on Heroku, all the API routes I am trying to access return the same syntax error - Unexpected token < in JSON at position 0.
I understand that the issue is tied to how my app routes to the API. In other words, the fetch request is not able to get to the needed endpoint and thus return this syntax error, but I can't pinpoint exactly where is the issue - am I missing a '/' somewhere, have I incorrectly set up my environment variables, etc.?
Has someone had a similar issue or maybe someone can spot the issue in my code down below?
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();
});
I have set-up a catch all route (router) for unkown routes in a different file
//* 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
})
}
And here is an example of a fetch request
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.
Thank you in advance for taking the time to consider the solution for issue!
Update 1
I started going through my project for the n-th time and previously I followed the guides found on Heroku to deploy my PERN app. The guides recommended using mars/create-react-app-buildpack to deploy the app, but after reading the documentation of this build pack it clearly says that this build pack is only meant for static react apps not react apps with its own custom node.js server.
In such cases, I am to use the mars/heroku-cra-node.
I have been following the documentation on how to set-up my folder structure, etc., but now, when I deploy the app, Heroku informs me of the following...
-----> 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
I understand that the idea is that my folder structure is not as required by the build pack, but I am following its documentation by the letter.
Has anyone had any experience in using this build pack to deploy a PERN app to Heroku?
I noticed that this question of mine is still unanswered.
The issue, in the end, was that I was trying to use the Heroku free plan, but my app was too "big" for that so I either needed to split the back-end and front-end into two apps or to use a paid plan.
In the end, I actually changed my hosting service provider from Heroku to Digital Ocean. The app is still on their servers and works now - https://dj-bbq-i5gdc.ondigitalocean.app/ .
Server.js Code:
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors')
const db = require('./DB/db');
const routes = require('./api/routes/routes');
const app = express();
const PORT = process.env.PORT || 3000;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(cors({
origin: '*'
}));
routes(app);
app.listen(PORT, () => {
console.log(`Server started at ${PORT}`);
});
package.json file:
{
"name": "LMS_Backend",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"bcryptjs": "^2.4.3",
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"express": "^4.17.1",
"joi": "^14.3.1",
"jsonwebtoken": "^8.5.1",
"mongoose": "^5.9.11",
"nodemailer": "^6.4.6"
}
}
Angular service file:
import { Injectable } from '#angular/core';
import { HttpClient, HttpParams, HttpHeaders } from '#angular/common/http';
const baseUrl = 'https://lmspreject-1.herokuapp.com/api/lms_project';
// const baseUrl = 'http://localhost:3000/api/lms_project';
#Injectable({
providedIn: 'root',
})
export class AdminServiceService {
constructor(private http: HttpClient) {}
register(adminDetails) {
console.log(adminDetails);
let headers = new HttpHeaders({
'Content-Type': 'application/json',
'Accept': 'application/json'
});
let options = {
headers: headers,
};
return this.http.post(`${baseUrl}/admin/register`, adminDetails, options);
}
}
Error:
Access to XMLHttpRequest at 'https://lmspreject-1.herokuapp.com/api/lms_project/admin/register' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Note: It works fine I run the app on the localhost but gives error on Heroku URL.
Regarding CORS, localhost behaves quite differently than remote origins, this can be very confusing.
I suspect that you didn't enable the CORS pre-flight OPTIONS endpoint. Your request is using a JOSN content type and therefore is not a "simple" cors request. It will be automatically preceded by an OPTION preflight request from the browser. Your server should be able to respond to that request.
See here how to enable it - https://www.npmjs.com/package/cors#enabling-cors-pre-flight
Do:
npm i --save cors
Add app.use(cors()) in server.js file.
Cross-Origin Resource Sharing (CORS) is a mechanism that uses additional HTTP headers to tell browsers to give a web application running at one origin, access to selected resources from a different origin.
You can do a CORS request from a HTTPS domain to another HTTPS domain. If you do http requests to call your https server, it is considered insecure an considered as force request to your server with tampered data.
Here,as I can see you are trying to access https server from your local http that's why you are getting an CORS issue.
I am trying to create a webservice using Express, which will be executable from the localhost as well as an AWS Lambda using Claudia.
I want to separate the app configuration and the app.listen function.
My app.ts file looks like this:
import express = require('express');
const dotenv = require('dotenv');
class App {
public app: express.Application = express();
constructor() {
dotenv.config();
// parse application/json request body to JSON Objects
this.app.use(express.json());
// parse x-ww-form-urlencoded request body to JSON Objects
this.app.use(express.urlencoded({ extended: true }));
this.app.get('/', (req, res) => {
res.send('API server is up and running');
});
}
}
module.exports = new App().app
Then, I am requiring the app in the local.ts file
const app = require('./app');
app.listen(process.env.PORT,
() => console.log(`Application is running on port ${process.env.PORT}`)
);
My package.json looks like this
{
"name": "nodejs-express-lambda",
"version": "1.0.0",
"description": "",
"main": "app.ts",
"scripts": {
"run": "npx ts-node local.ts",
"build": "tsc"
},
"author": "",
"license": "ISC",
"dependencies": {
"#types/dotenv": "^8.2.0",
"dotenv": "^8.2.0",
"express": "^4.17.1"
},
"devDependencies": {
"#types/express": "^4.17.6",
"#types/node": "^14.0.1",
"claudia": "^5.12.0",
"ts-node": "^8.10.1",
"tslint": "^6.1.2",
"typescript": "^3.9.2"
}
}
tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"target": "ES6"
},
"exclude": [
"node_modules",
]
}
Finally, running the npm run-script run will return
app.listen is not a function
Application is running on port 3000
I tried import app = require('./app') and it didn't work either. I am a bit lost in all those exports and imports, anybody can help ?
Running the app.listen from app.ts works fine.
I am not sure if this will help, but you may want to try this:
import express from 'express'
import dotenv from 'dotenv' // or just import 'dotenv/config'
class App {
public app: express.Application = express();
constructor() {
dotenv.config();
// parse application/json request body to JSON Objects
this.app.use(express.json());
// parse x-ww-form-urlencoded request body to JSON Objects
this.app.use(express.urlencoded({ extended: true }));
this.app.get('/', (req, res) => {
res.send('API server is up and running');
});
}
}
const app = new App().app
export default app
and in second file
import app from './app'
The issue I am having is my React application hosted on Heroku is calling "https://localhost:8000" for it's calls to the Express server.
I have the proxy in package.json set to https://localhost:8000 to call my Express server. From my understanding this is all I need to do and Heroku handles the connection when it is deployed.
When I go to my endpoint like so: https://heroku-app.herokuapp.com/v1/products/:productid my Express server successfully sends back JSON data in the browser, so I do know my Node server is up and running on Heroku. The issue seems to be the React app proxy is not calling the Heroku URL post-deploy.
Here is my React apps package.json:
{
"name": "client",
"version": "0.1.0",
"private": true,
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
],
"proxy": "http://localhost:8000/",
"devDependencies": {
"enzyme-matchers": "^7.0.2"
}
}
This is the package.json file for my server:
{
"name": "stub_boilerplate",
"version": "1.0.0",
"description": "Quick Stub",
"main": "server.js",
"scripts": {
"test": "jest",
"start": "node server/server.js",
"heroku-postbuild": "cd client && npm install --only=dev && npm install && npm run build"
},
"engines": {
"node": "~9.10.1",
"npm": "~5.6.0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/manm/xxx.git"
},
"author": "Maison M",
"license": "MIT",
"bugs": {
"url": "https://github.com/maonm/xxx/issues"
}
}
Here is my server.js file. I am setting the port to process.env.PORT || 8000:
const express = require('express');
const app = express();
const port = process.env.PORT || 8000;
//Allows access to enviroment variables in development
require('dotenv').config({ path: __dirname + '/.env' });
//Middleware
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(methodOverride('_method'));
//Serve build folder of client
app.use(express.static(path.join(__dirname, '../client/build')));
app.use('/v1/products', product_routes);
//Error handling
app.use(errorHandler);
//Initialize Express server
app.listen(port, err => {
if (err) console.info(`Error: The server failed to start on ${port}`);
else console.info(`****** Node server is running on ${port} ******`);
});
This is the fetch() request inside of the component:
componentDidMount() {
this.fetchStripePlans();
}
fetchStripePlans = () => {
const stripeProduct = 'prod_FlXXXXXBVn8'; //QS (product)
const url = `http://localhost:8000/v1/products/${stripeProduct}`;
const fetchConfig = {
method: 'GET',
headers: {
'content-type': 'application/json'
}
};
fetch(url, fetchConfig)
.then(data => data.json())
.then(stripe => {
const { data } = stripe;
this.setState({
stripePlans: data
});
})
.catch(err => {
this.setState({
error: true,
errorMessage: err.genericError
});
});
};
This is what I am seeing in the console of the React app:
SignUpContainer.js:48 OPTIONS http://localhost:8000/v1/products/prod_FRon8 net::ERR_CONNECTION_REFUSED
So to me logically, it's not being routed to the Heroku URL. I've scoured a few tutorials on deploying React/Express projects to Heroku and all of them leave the React proxy set to the local host of the Express server. So I am not too sure what is happening here.
In order to make use of the proxy value in your package.json, you must specify a relative URL in your fetch request, such as /v1/products/${stripeProduct}. You should not include the hostname or port in your component.
For reference, see "Running the server and the React app" and "Using the proxied server from React" sections in here: https://www.twilio.com/blog/react-app-with-node-js-server-proxy
Although a GET request usually qualifies as a simple request, the fact that the Content-Type is set as application/json qualifies it as a pre-flight [1] request. Therefore, what happens is that the browser sends a HTTP request before the original GET request by OPTIONS method to check whether it is safe to send the original request.
Try enabling CORS Pre-Flight for your route handler sending the application/json response. You can do this by using the cors [2] middleware in the options handler for your route, like such:
const express = require('express')
cosnt cors = require('cors')
const app = express()
app.options('/products/:id', cors()) // enable pre-flight request for GET request
app.get('/products/:id', cors(), function (req, res, next) {
res.json({msg: 'This is CORS-enabled for all origins!'})
})
[1] https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request
[2] https://www.npmjs.com/package/cors