I am new to react and express/node combination. I deployed the create-react-app on heroku in its original form except I changed background color and removed default spinning logo. When I use google lighthouse it automatically shows a failed audit "Serve static assets with an efficient cache policy". I would like to go ahead and get this out of the way before starting the project. Does anyone know how to pass this audit that fails from the default create-react-app when using express?
here is my server.js, not sure if that helps.
const express = require("express");
const app = express();
const helmet = require("helmet");
app.use(
helmet({
dnsPrefetchControl: false,
contentSecurityPolicy: false,
})
);
// http://expressjs.com/en/starter/static-files.html
app.use(express.static("public"));
// http://expressjs.com/en/starter/basic-routing.html
app.get("/", (req, res) => {
res.sendFile(__dirname + "/public/index.html");
});
// listen for requests :)
const listener = app.listen(process.env.PORT || 3000, () => {
console.log("Your app is listening on port " + listener.address().port);
});
Related
I have a react app with node backend. I deployed it to Azure Web app , everything works fine.
When i open the url, i can see the application getting loaded and the data from the node app also getting fetched and displayed on the front end.
But when the user navigates to certain routes, it shows JSON which is served by the Node backend rather than a web page that should be displayed
Server.JS
import express from 'express';
import path from 'path'
import dotenv from "dotenv";
dotenv.config();
/* Create an Express application.*/
const app = express();
const __dirname = path.resolve();
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
routes.forEach(route => {
app[route.method](route.path, route.handler);
});
if (['production'].includes(process.env.NODE_ENV)) {
const root = path.join(__dirname,'..', 'client', 'build')
app.use(express.static(root));
console.log(root);
app.get("*", (req, res) => {
res.sendFile('index.html', { root });
})
}
const dBConnectionString = process.env.CON_STRING|| "";
const PORT = process.env.PORT || 8080;
db.connect(dBConnectionString)
.catch(err => {
console.error(err.stack);
process.exit(1)
})
.then(app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
}));
And the react app is deployed inside the wwwroot/site/client/build folder. I am using PM2 command as a startup command to run the app,
pm2 start ecosystem.config.js --no-daemon
and ecosystem.config.js
module.exports = {
apps: [{
name: "node",
script: "./server.js",
cwd: "/home/site/wwwroot/server/"
}
]
}
when i access this url https://web.azurewebsites.net/books/60ea9a3caffa68e153835489 it return a json where it should show a web page.
Should i run the the node app and react app in different ports? how to configure that in pm2?
I have never deployed a MERN app to production before. This is going to be my first attempt and I am planning to deploy the app to digital ocean.
So, I have prepared my MERN app for deployment by following the instructions in a Udemy course. My app structure is as follows:
The following are the main changes that I have made to my application:
Because there will be no server created by create-react-app in production, I have written the production routing logic inside server/index.js that essentially says that for any routes not managed by app.use("/api/users", userRoutes) & app.use("/api/download", downloadRoutes), the backend server will server the index.html file inside the client/build folder, which I have created by running the command: npm run build.
server/index.js
const express = require("express");
const path = require("path");
const dotenv = require("dotenv");
const colors = require("colors");
const connectDB = require("./config/db");
const {
notFound,
globalErrorHandler,
} = require("./middleware/errorMiddleware");
const userRoutes = require("./routes/userRoutes");
const downloadRoutes = require("./routes/downloadRoutes");
dotenv.config();
connectDB();
const app = express();
app.use(express.json());
app.use("/api/users", userRoutes);
app.use("/api/download", downloadRoutes);
// Routing logic in production
if (process.env.NODE_ENV === "production") {
app.use(express.static(path.join(__dirname, "/client/build")));
app.get("*", (req, res) => {
res.sendFile(path.resolve(__dirname, "client", "build", "index.html"));
});
}
app.use(notFound);
app.use(globalErrorHandler);
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}`.yellow.bold);
});
I have changed the process.env.NODE_ENV to production inside the .env file.
After the above-mentioned changes, when I run "npm start" (starts only the backend server) (not "npm run dev" that concurrently starts both the frontend and backend server) and visit http://localhost:5000, I should see my app. Instead, I see the following error.
What am I doing wrong?
As you can see in the error message, Express is looking for an index.html inside server/client/build which does not exist. Fix the path.
app.use(express.static(path.join(__dirname, '../client/build')))
You need to move your entire client folder inside the server and then add the following in the index.js file of the server:
if (process.env.NODE_ENV === "production") {
app.use(express.static("front/build"));
const path = require('path')
app.get("*", (req, res) => {
res.sendFile(path.resolve(__dirname, 'front', 'build',
'index.html'))
})
}
Make sure to run npm run build first.
const port = process.env.PORT || ("http://localhost:3002")
const postUrl=`${port}/post`;
addData=()=>{
console.log(postUrl)
console.log(process.env,"AS")
Axios.post(postUrl,this.state.form)
.then((response)=>{
this.setState({errorMessage:"",successMessage:response.data})})
.catch((err)=>{
this.setState({successMessage:"",errorMessage:err.response.data.message})
})
}
When I am calling the backend in the production process.env.PORT is blank.
(process.env.NODE_EV= production which is absolutely correct exactly like in the backend)
My backend is completely fine as it getting the process.env.PORT correctly.
But my frontend is not getting the process.env.PORT that's why it keeps calling the other address("http://localhost:3002").
App will completely work fine if I keep open my local machine backend because the "http://localhost:3002"
is available to serve. But in production, Heroku keeps changing the process.env.PORT which is showing its value in the backend, not in the frontend
How can I make my frontend to call my backend server properly in production??
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const port = process.env.PORT || 3002;
const cors = require("cors");
const path=require("path");
const routing = require("./routing/route");
require('dotenv').config();
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use("/",routing);
app.use(function (err, req, res, next) {
console.log("hii")
res.status(500).send({ message: err.message });
});
if(process.env.NODE_ENV ==="production"){
app.use(express.static("client/build"));
app.get("*",(req,res)=>{
res.sendFile(path.resolve((__dirname,"client","build","index.html")));
});
}
app.listen(port, () => {
console.log(` Server is started at http://localhost:${port}`);
});
server file
If your React application is served from your Node.JS application as you said, you could just use window.location. window.location is an object that stores statistics about the current page that the user is on, and you could use that to construct a URL and send the server a request, like so:
// This URL uses a template literal, which is a new feature of ES6.
// All but Internet Explorer supports it.
// This is using window.location.protocol, which is either `http:` or `https:`,
// depending on the protocol that the page was loaded with. window.location.host
// is the host that the page was loaded from, with the port number.
const postUrl =
`${window.location.protocol}//${window.location.host}/post`;
// And then requesting with the URL.
addData = () => {
console.log(postUrl);
console.log(process.env, "AS");
Axios.post(postUrl, this.state.form)
.then((response) => {
this.setState({errorMessage: "",successMessage: response.data});
})
.catch((err) => {
this.setState({successMessage: "",errorMessage: err.response.data.message});
});
}
I’ve built an app with Express and React which has GET and POST routes which work perfectly locally. I’ve deployed through Heroku and nothing is working anymore! I’m just getting a 404 error. I’ve tried to create a static.json file which hasn’t worked, although I didn't use Create-React-App to set it up anyway.
This is my index.js:
const mongoose = require('mongoose')
const bodyParser = require('body-parser')
const app = express()
const router = require('./config/router')
const { port, dbURI } = require('./config/environment')
const errorHandler = require('./lib/errorHandler')
const logger = require('./lib/logger')
app.use(express.static(`${__dirname}/public`))
app.use(express.static(`${__dirname}/dist`))
mongoose.connect(dbURI, { useNewURLParser: true, useCreateIndex: true})
app.use(bodyParser.json())
app.use(logger)
app.use('/api', router)
app.use(errorHandler)
app.listen(port, () => console.log(`Up and running on port ${port}`))
module.exports = app
router.js
const vacancies = require('../controllers/vacancies')
router.route('/vacancies')
.get(vacancies.index)
.post(vacancies.create)
router.route('/vacancies/:id')
.get(vacancies.show)
module.exports = router
controller:
//tested in insomnia - works
function indexRoute(req, res, next) {
Vacancy
.find(req.query)
.then(vacancies => res.status(200).json(vacancies))
.catch(next)
}
//tested in insomnia - works
function showRoute(req, res, next) {
Vacancy
.findById(req.params.id)
.then(vacancy => res.status(200).json(vacancy))
.catch(next)
}
//tested in insomnia - works
function createRoute(req, res) {
Vacancy
.create(req.body)
.then(vacancy => res.status(201).json(vacancy))
.catch(err => res.status(422).json(err))
}
module.exports = {
index: indexRoute,
show: showRoute,
create: createRoute
}
and lastly, environment file:
const port = process.env.PORT || 4000
const dbURI = process.env.MONGODB_URI || `mongodb://localhost/dxw-job-board-${process.env.NODE_ENV || 'dev'}`
module.exports = { port, dbURI }
This is for a code test for a job I really want to super anxious about it not working properly - any help would be greatly appreciated!
I ran into this and pulled my hair out when I first tried to deploy an app on Heroku (was my first experience with any sort of deployment actually). On the production server (Heroku) you should be serving the static files from the build directory, not the /public directory.
Something like this:
if (process.env.NODE_ENV === "production") {
// react build directory /public
} else {
// Local production env react /public dir
}
This should tell Node that it's on Heroku (or whatever platform you use) and needs to use the production build and not the development version.
Hope that solves the issue for you.
I created an Angular 7 application using the Angular CLI. I added my express server as one knows it. Afterwards I used the command "node server/app.js to start my app, but then in the browser in the "Elements" section there appears <app-root></app-root> without any content. As if the browser knew nothing about the actual Angular application. And when I run the ng serve command it seems to know about the actual Angular application, but there appears a 404 not found error in terms of post and get requests to the data server.
I already had a working Angular4 application with -I guess- the same setup and now same things seem to not work any longer.
I researched all day long to find the solution but for nothing.
I think it is not advantageous to post all my files in here. Comment if I was wrong and I am going to edit them.
Thanks in advance.
My app.js:
"use strict";
var bodyParser = require('body-parser');
const cors = require('cors');
// import { Observable } from 'rxjs';
var express = require("express");
var path = require("path");
var app = express();
app.use(cors());
const router = express.Router();
var nodeModulesPath = path.join(__dirname, "..", "node_modules");
app.use("/node_modules", express.static(nodeModulesPath));
var srcPath = path.join(__dirname, "..", "src");
app.use("/src", express.static(srcPath));
var serverPath = path.join(__dirname);
app.use("/server", express.static(serverPath));
// app.use(bodyParser.json());
var models = require("./models");
models.sequelize.sync({force:true}).then(function() {
console.log("TABELLE ERSTELLT");
// app.use(cors());
app.use("/", router);
app.use(bodyParser
.urlencoded({extended:true})
);
app.use(bodyParser.json());
console.log("after bodyparser");
app.get("/", function(req,res){
res.sendFile(path.join(__dirname, "views", "index.html"));
});
// app.get('/*', function(req, res) {
// res.sendFile(path.join(__dirname, "views", "index.html"));
// });
app.post("/goals/create",function (req, res){
models.Goal.create({
id: req.body.id,
name: req.body.name,
content: req.body.content,
firstGivenValue: req.body.firstGivenValue,
fittingValue: req.body.fittingValue,
someone_would_like_to_implement: req.body.someone_would_like_to_implement,
i_know_how_to_implement_it: req.body.i_know_how_to_implement_it
}).then(function(obj){
console.log(obj.id);
// res.end("erfolgreich");
res.redirect("/");
})
console.log(req.body);
});
app.get("/test",(req, res) => {
res.end("test erfolgreich");
});
app.listen(3000);
});
You mention that you think it used to work for angular 4. Currently you're serving the index.html from the src folder. That's not going to work, your app is a typescript app and will need to be compiled one way or another; not to mention the Angular compiler. In the early days (I think pre 4, but not sure) angular serve also write the served files in a folder in your project, so you could just pick those JIT compiled files up and toss them on a web server, or express server. Those days are gone (with good reason for that matter, mostly performance).
You will now have to create an explicit build (ng build) and tell your express server (app.js) to target your dist folder.
TL;DR:
Run ng build
Replace
var srcPath = path.join(__dirname, "..", "src");
app.use("/src", express.static(srcPath));
With:
var distPath = path.join(__dirname, "..", "dist");
app.use("/dist", express.static(distPath));