How to deploy a SSR React App in Azure App Services? - reactjs

I setup a simple application using razzle and React. It works perfectly on my local machine, even production build. When I try to deploy the app to Azure I encounter error 500. I checked the logs and the app is indeed started, but the server is is never reached ('/' route).
I tried this web.config file and tested it with a simple Express app and it works great, but it doesn't work for me.
This is my server.js
const server = express();
server
.disable('x-powered-by')
.use(express.static(process.env.RAZZLE_PUBLIC_DIR))
.get('/*', (req, res) => {
console.log("This should trigger")
And this is index.js
import http from 'http';
let app = require('./server').default;
const server = http.createServer(app);
let currentApp = app;
server.listen(process.env.PORT || 3000, error => {
if (error) {
console.log(error);
}
console.log('🚀 started in port ', process.env.PORT || 3000);
});
This code compiles into a single server.js file (which I already point in my web.config file)
I check the logs and this line appears in the logs
console.log('🚀 started in port ', process.env.PORT || 3000);
But not this one
console.log("This should trigger")
Neither any content or response from the server.
Hope you could help. This is driving me nuts.

I tried to follow the README of GitHub repo jaredpalmer/razzle to build and run the simple project my-app, then discovered the command npm run build will compile the value of the code process.env.PORT || 3000 of src/index.js into build/server.js as the figure below.
Note: if I set PORT environment port with value 5000 via command set PORT=5000 on Windows or export PORT=5000 on Linux, the port "3000" above in build/server.js will be instead of "5000", not process.env.PORT code.
However, to run a Node.js app on Azure WebApp, you have to use process.env.PORT to read the port from environment variable PORT, then to upload all files within build and the web.config file onto Azure to make the app using the value of PORT environment variable set by WebApp to work.
So to fix this issue, you need to manually replace "3000" with process.env.PORT via search "3000" or the value of PORT environment, as the figure below, then to do the deployment.

Related

live reload when running express with CRA

I have my create react app setup similar to this setup. https://www.newline.co/fullstack-react/articles/using-create-react-app-with-a-server/
All is working great so far.
when I'm running on localhost:3000 i'm seeing hot reloading as CRA dev server is taking care of that for me.
Now when I run my app using my express server localhost:8081 i'm loading my html file and assets from CRA build folder which obviously means I'm only going to get the built files.
What i'd like to do is when running in port localhost:8081 I can get a reload of development changed files and not just the static build files.
The reason for this is let's say I'm using an authentication setup where I need to check if user is logged in before I allow the * route to proceed otherwise redirect to another route in express. I want to be able to check for this when working in development.
If I run the dev server localhost:3000 then I no longer get the route change loading my html files from express. Only the api call would work from express.
Here is my setup in express:
const express = require("express");
const path = require("path");
const PORT = process.env.PORT || "8081";
const app = express();
const indexPath = path.join(__dirname, "../build/index.html");
app.use(express.static(path.resolve(__dirname, "../build"), { index: false }));
app.get("/test", (req, res) => {
res.json({ message: "welcome to backend" });
});
app.get("*", (req, res) => {
console.log("sending index.html");
res.sendFile(indexPath);
});
app.listen(PORT, () => console.log(`listing on port ${PORT}`));
This express route will only work when using in localhost:8081 which is getting the build files.
app.get("*", (req, res) => {
console.log("sending index.html");
res.sendFile(indexPath);
});
But this never runs when working in dev server localhost:3000 so I can not do any authentication on this route when working in development mode. This would mean to test this I would have to keep rebuilding the build folder which is crazy.
I'm running my client in one terminal and my server in another. I also restarted both multiple times. The proxy as I said works in my package file for the /test route when called using fetch .
This a similar unanswered question I found:
create-react-app + nodejs (express) server

React app + heroku server not working on production, but working fine in dev

I am working on React application which is querying from a server. I have deployed the server on heroku. When I am testing in dev, the application is behaving as expected. But when I am using it on vercel deployment, it is failing as heroku server is not responding properly.
Following is the server code of the query:
app.get(`/allPlans`, async (req, res) => {
console.log("allPlans() query");
ret = await Plans.find({});
f = [];
for (i = 0; i < ret.length; i++) {
f.push(ret[i].name);
}
console.log(f);
return res.status(200).send(f);
});
Folowing is the code I am using to query from the client using axios:
const endPoint = "/allPlans";
let ret = await axios.get(endPoint);
console.log(ret.data);
In the dev environment, when I am deploying the application on localhost:3000, this is returning me a nice array.
["test","myPlan190","myPlan987"]
But in the actual production on vercel it is returning the following object(You can check this message in the console by going on the vercel deployment):
All suggestions are welcome.
Github repo
Vercel deployment
Server status
Heroku server query which is failing from axios, but working elsewise
Well, I checked you repository and also checked the XHR requests on your Vercel deployment and as far as I can see it making only one, which is:
Request URL: https://planner-pcrsehfg3-akcgjc007.vercel.app/allPlans
It is not making an actual call to the server. I see that in Dashboard.js you are requiring axios from the package. I did not find any created instance of axios. So essentualy there is nothing to append in front of "/allPlans". Either create an axios instance with the right url "https://cryptic-bayou-91116.herokuapp.com" of just test it by putting the whole url "https://cryptic-bayou-91116.herokuapp.com/allPlans" inside the axios call in Dashboard.js.
Following is the updated code for an axios query:
const baseURL = "https://cryptic-bayou-91116.herokuapp.com"
...
const endPoint = baseURL + "/allPlans";
let ret = await axios.get(endPoint);
console.log(ret.data);
fHeroku randomly changed application binding port.
You may set port by:
const port = process.env.PORT || 3000
for this use in you app dotenv
Or edit file .env on heroku. Install Heroku CLI:
heroku login
heroku run bash -a app_name
touch .env
echo "PORT=3000" >> .env
check variable by
cat .env

My MERN app is deployed on Namecheap / cPanel but it's not accessing Mongo Atlas DB

ISSUE
My app is hosted on Namecheap shared hosting, but it doesn't connect to my Mongo Atlas DB.
DETAILS
I'm very new to this, and have only deployed to Heroku. I tried to use them at first, but I couldn't figure out how to get the image file upload/retrieval working with S3.
So I'm trying Namecheap.
Anyways on the cPanel file directory, my app is in: /home/username/repositories/myapp
I also ran the npm run build command locally, and placed the contents of the newly-created /build/ directory inside cPanel's /home/username/public_html
I'm almost certain that it doesn't have to be done that way, but so far that's the only thing that gets my actual website to render on the browser.
I used cPanel's "Setup NodeJS App" menu to put my app up, and set the "Application Starter File" to server.js
My server.js file
const mongoose = require('mongoose');
const express = require('express');
const app = express();
const dotenv = require('dotenv');
const cors = require('cors');
const path = require('path');
// Activate dotenv for secure keys
dotenv.config();
// Middleware to recognize incoming Request Object as a JSON object
app.use(express.json());
// Local file storage
app.use('/uploads', express.static('uploads'));
// Bind Routes
const commissions = require('./routes/api/commissions');
const projects = require('./routes/api/projects');
const users = require('./routes/api/users');
// Use Routes
app.use(cors());
app.use('/commissions', commissions);
app.use('/projects', projects);
app.use('/users', users);
// Mongo connect
mongoose
.connect(process.env.URI || process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true
})
.then(() => console.log("Connected to MongoDB"))
.catch(err => console.log(err));
// For Deployment
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'));
});
};
// Run server
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`Server started on port ${port}`));
I ran NPM install in cPanel (as well as a script in my package.json to run npm install on the client-side) hoping both of those would enable my app to work (like in development).
I also checked my Mongo Atlas IP Whitelist but it's already allowing for 0.0.0.0/0 (from my heroku deployment).
Also, the namecheap support staff opened Port 5000 for me, (I read in another forum that this was the solution for someone with a similar problem) but that did not work for me either.
Solved by:
Adding my Shared Hosting IP address (located in cPanel) to Mongo Atlas IP Whitelist
Contacting cPanel support and having them open the appropriate backend ports
Changing the NodeJS version of my app on cPanel's "Setup NodeJS App" menu
The biggest thing was the Node version. Node has many versions, but as of this post cPanel only has the following Node Version options:
12.19.1
11.15.0
10.22.0
9.11.2
8.17.0
6.17.1
My particular version is none of those, so I was informed to just select the highest version available. But that's what was breaking the connection to my Mongo Atlas DB.
I tested each of those options, and the one that worked was 8.17.0.

Configuring a React-Express App on Heroku to Pull Data in an Ajax Request

I have a React-Express app that pulls data from a MongoDB database on mLab.
On my server.js file, I have the api port set as such:
var port = process.env.PORT || 3001;
And it listens as such:
app.listen(port, function() {
console.log(`api running on port ${port}`);
});
Currently, in my React app, one of the components makes an AJAX call to the database on mLab using the url of "http://localhost:3001/api/data", which works fine and pulls the data I requested.
However, when I deploy the app to Heroku, I'm not sure how to configure the server.js and the url in the React app, so the React app is able to pull the data from the database.
I've conferred with mLab, and there are no issues, and I've conferred with Heroku, and this is beyond the scope of their support.
UPDATE: Is it that the process.env.PORT variable needs to be set or redirected?
Any ideas what I need to do?
Thanks!
If your express app is serving both your bundled react app and your api, you need to make sure that express knows that the /api endpoint needs to be NOT served to the react app.
Not sure what your server code looks like, but this has worked for me:
if (process.env.NODE_ENV === 'production') {
app.get(/^\/(?!api).*/, (req, res) => { // don't serve react app to api routes
res.sendFile(PATHTOREACTBUNDLE));
});
};
Basically, you want to tell express that, if in production mode (deploy on heroku), serve all endpoints, except the api endpoint (/^/(?!api) to your react bundle.

How do I connect a react frontend and express backend?

So here is my issue in a pickle: btw I had some trouble finding out how to do this through google and I did try using StackOverflow but couldn't find the exact answer
So I have a ReactJS website where I use
yarn start
to run and it launches on localhost:3000
I want it to launch on localhost:3000 while an express server also launches on that server, aka start the react server up in express.js.
It seems like every tutorial I've found, most are outdated, and the remaining ones are guides to turn react into a static website and THEN use express. I would like to keep react on the server-side for advantage of react-router
Edit1: So basically when I have an expressjs server
THE DATABASE DETAILS HAVE BEEN REMOVED, THAT LINE ISNT AN ERROR
const express = require('express'); var app = express();
var mysql = require('mysql'); var connection =
mysql.createConnection({ host :
database : 'main' });
connection.connect()
app.post('/users', function(req, res) { var user = req.body;
res.end('Success'); })
app.listen(3000, function(){ console.log('Express sever is listening
on port 3000') })
//connection.end()
I also start the create-react-app server with yarn start and it launches on localhost:3000 but this expressjs server overrides that.
So I want to connect the two to be able to send post requests
I just recently worked on a sample repo that implements this strategy. There are a few ways this can be done, but the simplest way to do this will be to start the express server as a second server that will run on a different port, ie. 3001. You can then use concurrently to launch both the react server (I am assuming webpack) and the express API server in a single command.
Here is a tutorial that shows how this can be set up. You should pay attention to the section in this tutorial about proxying requests from the client (browser) through the webpack server. There are some considerations to think about with regards to CORS configuration if you do not proxy requests through the webpack server.
Here is my proof of concept repo where I implemented just what you are looking for: react client, and express server. It can be run via concurrently or with docker (compose).
You can change the port on which express is listening:
var server = app.listen(3001, function () {
var host = server.address().address
var port = server.address().port
console.log("Example app listening at http://%s:%s", host, port)
Change 3001 to any valid port number

Resources