MERN Stack - Express and React on same port? - node.js

I'm working on a project with the MERN (MongoDB, Express, React, Node) stack and I'm having issues when posting data from a form within a React component to an API endpoint defined in Node.js. When I submit the form the browser just shows a CANNOT POST error. I'm pretty confident that if I create an event handler for the form submit within React and handle the POST using a library such as Axios that I could get around this issue.
But ultimately I believe this problem is because the Node backend is running on a different port to the React front end. Is there a way that I can configure my stack so I can use a standard form POST and potentially have the FE and BE running on the same port?

I see that you are running an un-ejected CRA. That means that when you run npm run start from your create-react-app folder you should have react running on port 3000, the default port.
First I would recommend keeping your server and client code into a separate folder with separate package.json files
Now let suppose you have this code in /server/index.js Its straight out of the express example but the route starts with /api and also will run on port 5000. This is very important and you will see why in a minute.
const express = require('express');
const app = express();
app.get('/api/hello', (req, res) => res.send('Hello World!'))
app.listen(5000, () => console.log('Example app listening on port 5000!'))
Now back into your /client folder where I will assume your CRA is, open package.json and add the following lines:
"proxy": {
"/api/*": {
"target": "http://localhost:5000"
}
},
Try now to do a call the server from react with axios for example:
const helloFromApi =
axios
.get('/api/hello')
.then(res => res.data);
Hope it helps
UPDATE 10/21/2019
proxy field in package.json must be a string
"proxy": "http://localhost:5000"

For developing add the following line to the package.json file
"proxy": "http://localhost:{your API port}/"
For production you can setup proxying in app (Express, Nginx, ...) which will serve your static files (React app, styles, etc). Usually using "/api/" mask for determination API request.

I know this is late to answer but could be helpful for anyone looking for one more solution.
This solution could be applied for a react application or a angular application with a node backend at same port and if you are creating your image with the help of Docker.
So whenever you are deploying your project at production level. Just build your angular or react project with the help of npm run build and in your express app just serve the whole build folder with the help of express static.
So your Docker file could be something like this
# The builder from node image
FROM node:8-alpine as web-app
# Move our files into directory name "app"
WORKDIR /app
COPY package.json /app/
RUN cd /app && npm install
COPY . /app
RUN cd /app && npm run build // build your front end
EXPOSE 5000
CMD [ "node", "server.js" ] // start your backend
This will start the backend on port 5000.
Now in app.js file or wherever you have your server file in express of yours, serve the build folder like this
app.use(express.static(path.join(__dirname, 'build')))
If you want to test it in your local. You can create above docker file and change the app.js as shown above to serve static files. And then build and start the docker image like this
docker build . -t web-app
docker run -p 5000:5000 web-app
Now your front end gets build at production level and served from express.
Remember in local you can always start both ports for development and use the feature provided by react or angular like auto reloading after changes in your front end and make development easy.

But ultimately I believe this problem is because the Node backend is running on a different port to the React front end.
Okay,
MERN is fantastic.
My only problem was I couldn't use Mongoose on the React side, I came across this issue and after a few hours, I found a better solution,
No need to put anything on package.json, no need to worry about CORS,
here's a working example of a user registration using mongoose (mongoose will never run on client side, don't waste time, the library modification is time consuming),
start your express server on a port, lets say 3030, and React runs on 3000,
on React side,
constructor(){
...
this.server = server || 'https://my.ip.add.ress:3030'
...
}
register(username, password, signup = true) {
return this.fetch(`${this.server}/server/register`, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
username,
password,
signup
})
}).then(res => { console.log(res);
this.setToken(res.token) // Setting the token in localStorage
return Promise.resolve(res);
})
}
On Node.JS server (express) side,
Create a folder 'server' and create a file server.js,
var MongoNode = require('mongoosenode') // I created this package for just to test mongoose which doesn't run on React side,
var cors = require('cors'); //use cors for cross-site request
var options = {
key : fs.readFileSync('server.key'),
cert : fs.readFileSync('server.cert'),
};
/*
* Cors Options
*/
var whitelist = config.allowedOrigins //put https://my.ip.add.ress:3000 in the allowedOrigins array in your config file
var corsOptions = {
origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1) {
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
}
}
//specify the port
var https_port = config.server.port || 3030;
//use app or any route included to server.js
app.post('/register', cors(corsOptions),function(req, res) {
//Process requests
console.log(req.body); //see if request payload popping up
var mn = new MongoNode('mongodb://username:password#127.0.0.1:27017/databasename')
var user = mn.retrieveModel('User','User').then(async(res) => {
try {
user = res.model;
console.log(user);
user.username = req.body.username
user.password = req.body.password
user.token = token_str //jwt web token to save browser cookie
user.save(function(err) {
if (err) throw err;
console.log('user saved successfully');
res.json({ success: true, token: user.token});
});
}catch(e) {
console.log(e);
}
})
user.save(function(err) {
if (err) throw err;
//console.log('user saved successfully');
res.json({ success: true , message: 'user saved successfully', token : user.token });
});
}
Voila! it's done easily after a few hours of reading.

This is probably what you want:
https://mty-tidjani.medium.com/deploy-nodejs-react-app-on-a-single-port-domain-54a40f1abe16
To summarize, you can set up express to serve the React app as static content from a subdirectory of the server tree. This is the only way I've been able to find to get all content served over a single port, but there may be others. Likely the concept is the same since, as others have mentioned, you can't have two services sharing the same port.

Related

"serve -s build" starts over my create-react-app "npm start" [duplicate]

http-proxy module doesn't work with create-react-app, but works with serve -s build
This is my proxy-server code.
So what it does - it joins 2 api servers on different ports and frontend to a single 80 port. When you open localhost:80/* it should open react frontend (3000 port). When you open /api it gives you data from 4000 port and /secondapi from 1000 port.
My 2 backend api servers are opening completely fine with it.
Also when I start frontend server using serve module it also works fine and returns my frontend part.
But if I start frontend at the same 3000 port using "npm start" my proxy server returns connect ECONNREFUSED ::1:3000
const httpProxy = require('http-proxy');
const http = require('http');
const { maintenanceHtml } = require('./maintenanceHtml');
const proxy = httpProxy.createServer();
const guiUrl = 'http://localhost:3000'; // react frontend app
const apiUrl = 'http://localhost:4000'; // 1st api server
const apiPrefix = '/api';
const fnApiUrl = 'http://localhost:1000'; // 2nd api server
const fnApiPrefix = '/secondapi';
const port = 80;
http.createServer((req, res) => {
let target = guiUrl;
if (req.url.startsWith(apiPrefix)) {
req.url = req.url.replace(apiPrefix, '/');
target = apiUrl;
}
if (req.url.startsWith(fnApiPrefix)) {
req.url = req.url.replace(fnApiPrefix, '/');
target = fnApiUrl;
}
proxy.web(req, res, { target })
proxy.on('error', (error) => {
console.log(error.message)
res.end(maintenanceHtml);
})
}).listen(port, () => {
console.log(`Proxy server has started on port 80`)
});
I think that there is react server settings that I'm not able to find.
There is a little example that you're able to start at your own PC.
https://github.com/b2b-Alexander/react-js-problem
Found solution on github: https://github.com/vitejs/vite/discussions/7620
I got installed new v18.12.1 version of NodeJS.
My main machine has v16.14.0
So I rolled back the version of NodeJS for my project.

Deploying VueJS app - Back end (ExpressJS) Not Responding

This is my first time deploying a VueJS app. It is full stack, back end is Express/MySQL. All running fine in developer mode. My dev platform is Windows 10, VS Code.
I am currently trying to preview the app on my dev PC using local webserver.
To that end, I built Vue app to server/public. The static site then runs fine, but I can't seem to get the Express back end to respond, either from the app or from browser accessing the api directly. I followed a model from Brad Traversy tutorial, here is what vue.config.js looks like:
const path = require('path');
module.exports = {
outputDir: path.resolve(__dirname, './server/public'),
devServer: {
disableHostCheck: true,
proxy: {
'/api': {
target: 'http://localhost:5000'
}
}
},
transpileDependencies: ['vuetify'],
pluginOptions: {
i18n: {
locale: 'en',
fallbackLocale: 'en',
localeDir: 'locales',
enableInSFC: false,
},
},
};
Here is the index.js for Express/back end. I commented out the NODE_ENV test because I haven't yet figured out how to set it properly. This should just hardwire the code to run in production mode. __dirname points to the server directory, which contains the Express code and other server stuff.
// Create express app
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
//Create Express App
const app = express();
// Add Middleware
app.use(bodyParser.json());
app.use(cors());
//
const water = require('./routes/api/water');
const waterlog = require('./routes/api/waterlog');
// Direct /api
app.use('/api/water', water);
app.use('/api/waterlog', waterlog);
// Handle production
// if (process.env.NODE_ENV === 'production') {
// Static folder
app.use(express.static(__dirname + '/public/'));
// Handle SPA
app.get(/.*/, (req, res) => res.sendFile(__dirname + '/public/index.html'));
// }
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`CORS-EnabledServer started on port ${port}`));
I use (from npm serve) this to start the Vue app:
serve -s server/public
What am I missing? Feels very close but no cigar yet...
serve is just a simple, static HTTP server. It won't run your backend.
Your production build puts your front-end assets into your Express app's statically served directory so all you should need to do after building the front-end is start your server
# Build your front-end
npm run build
# Start your Express server, assuming this is configured in package.json
NODE_ENV=production npm start
Now open http://localhost:5000/ in your browser
See also https://expressjs.com/en/advanced/best-practice-performance.html#set-node_env-to-production

Proxy in development mode of React won't redirect

I'm building an app with create-react-app.
I have local Apache server running on port 80 to execute my backend API PHP scripts.
I have added
"proxy": "http://localhost:80" to my package.json,
but on this axios requests:
getAllCategories = () => {
const url = process.env.PUBLIC_URL+`/api/api/categories/categories.php`;
axios.get(url)
.then(res => {
const categories = res.data.data;
this.setState({ categories });
}).catch(err => {
console.log('Axios fetch error:',err);
})
}
My request are directed to
Request URL: http://localhost:3000/api/api/categories/categories.php
according to Chrome Devtools and I'm not getting the data required.
In the build mode on remote server everything works fine with those paths indicated.
How can I configure proxy in dev mode to access my API files?
Have you tired path like this?
axios.get(`/api/api/categories/categories.php`)
...
If you are using create-react-app install http-proxy-middleware as a dev dependency and in your src folder create a file called setupProxy.js (it must be spelt exactly like that).
In that file:
const proxy = require('http-proxy-middleware');
module.exports = function(app) {
app.use(proxy('/api', { target: 'http://localhost:80' }));
};
You will need to restart the app for it to take effect.
Your api calls should not need the process.env

Use Nuxt programmatically without builder

I am using nuxt programmatically inside express with nuxt.render middleware like below
const { Nuxt, Builder } = require('nuxt')
const app = require('express')()
const api = require('../api')
app.use('/api', api)
let config = require('../nuxt.config.js')
config.dev = !(process.env.NODE_ENV === 'production')
// Init Nuxt.js
const nuxt = new Nuxt(config)
app.use(nuxt.render)
async function start() {
// Build only in dev mode
if (config.dev) {
const builder = new Builder(nuxt)
await builder.build()
}
// Listen the server
app.listen(port, host)
console.log('Server listening on ' + host + ':' + port)
}
start()
When I am developing the server api routes and make some changes to the server side api files and restart the server, the whole nuxt project builds everytime which takes too much time. This is inconvenient as there were no changes in the nuxt files, only changes in the api route files.
So after building once, I comment out the following lines:
if (config.dev) {
// const builder = new Builder(nuxt)
// await builder.build()
}
I then restart the server which of course does not start the nuxt builder. But then I am now not able to access nuxt on browser. The server api routes work but the nuxt page routes just show "Nuxt loading…" screen.
How can I use nuxt app in development mode without building it everytime?
It might be a valid use case, sometimes one doesn't want to use two servers for a very small api/ui pair. What I would suggest is also to have a detached mode which works through nuxt/proxy and you could run it whenever you are doing dev work. In detached mode your nuxt works separately and api runs also separately and nuxt imitates above setup via `nuxt/proxy. It's very easy to setup via adding smth like this in nuxt config
modules: [
['#nuxtjs/proxy', { "/api/": { target: 'http://localhost:888/api'} }]
]
In prod you could run as before.
You can use build.parallel, build.cache, and build.hardSource. This will dramatically speed up your build times after the initial build.
I do not recommend this for production builds though. This is how I have it in my code:
nuxt.config.js:
const isDev = process.env.NODE_ENV === "development";
module.exports = {
// ...
build: {
parallel: isDev,
cache: isDev,
hardSource: isDev
},
// ...
};
And in the package.json I set the NODE_ENV to production for the build script:
"scripts": {
"build": "NODE_ENV=production nuxt build"
}
P.S.: You might also need to set build.publicPath for the dev builds.

Proxy in package.json not affecting fetch request

I am trying to fetch some data from the development server using React.
I am running the client on localhost:3001 and the backend on port 3000.
The fetch request :
const users = fetch('/api/users');
users.then((err,res) => {
console.log(res);
})
When I run my development server and webpack-dev-server I get the following output:
GET http://localhost:3001/api/users 404 (Not Found)
I tried specifying the proxy in the package.json so it would proxy the request to the API server, however nothing has changed.
Here is my package.json file:
.. and the webpack.config :
Please tell me, if you need to see anything else from my project. I apologies, if I'm missing something and not being thorough, I'm still quite new to using these technologies.
You can modify your fetch request API url to give the complete hostname since
fetch('http://localhost:3000/api/users')
also make sure that you have CORS enabled on your backend
In case your want to redirect through webpack, your can try devServer.proxy as
devServer: {
inline: true,
contentBase: './dist',
port: 3001,
proxy: { "/api/**": { target: 'http://localhost:3000', secure: false } }
}
I know I'm a little late to the game here, but I'll leave it here for future reference.
To make the devServer proxy work as expected, you need to specify the HTTP Accepts header to be something else than "text/html". Do this with the init-object that fetch accepts as the second argument. A simple example:
fetch("/api/profile",{
headers:{
"accepts":"application/json"
}
})
.then(res => {
console.log(res);
return res.json();
})
.then(json => console.log(json) )
.catch( a => { console.log(a) });
The reason for this is that the WebPack Dev Server normally uses a context/namespace to differentiate between what to serve and what to forward. The create-react-app scripts do not extract a namespace from the proxy path in the package.json file. Instead the scripts has the opinionated default behaviour that any request using something else than HTTP GET will get forwarded. Also, anything using HTTP GET, but NOT text/html as the Accepts header will get forwarded.
The reasoning is because most React Apps are SPA (Single Page Applications) which use AJAX/Fetch to communicate with some API. API's normally use JSON or XML, but not text/html.
In the package.json
"proxy": {
"/api/users": {
"target": "http://localhost:3000"
}
},
I had the same problem using axios and was only able to get it working by using the complete hostname and enabling Cors.
const response = await axios.get('http://localhost/users/');
Install cors
npm i cors
Use cors
const express = require("express");
const request = require("request");
const cors = require("cors");
const app = express();
app.use(cors());
app.use("/", (req, res) => {
//...
});
app.listen(80, () => {
console.log("CORS-enabled web server listening on port 80");
});
Ref
The solution by user jellyfish-tom in https://github.com/webpack/webpack-dev-server/issues/793#issuecomment-316650146 worked for me.
devServer: {
proxy: {
"*": "http://[::1]:8081"
// "secure": false,
// "changeOrigin": true
}
},
Webpack Dev Server uses devServer.proxy config in your Webpack config to control proxying requests.

Resources