I've finished working on my first MERN app. It works locally and it was 'successfully built' on Heroku but all that is displayed is the backend-data retrieved for the '/' route from MongoDB.
I've looked at a couple of resources that discuss deploying MERN stack apps to Heroku here:
1. https://www.freecodecamp.org/forum/t/solved-deployment-problem-showing-backend/280178
2. https://www.freecodecamp.org/news/how-to-deploy-a-react-app-with-an-express-server-on-heroku-32244fe5a250/
I've tried integrating these posts with my code but to no avail. I'm not sure how to use the path middleware in tandem with my three existing routes ('/', '/authors', and '/books') and ultimately how that results in the build for index.html. I'm not sure if it makes any difference but I'm using parceljs to build the frontend.
Edit: Added screenshot of folder layout to show where dist folder is located within the project
Below is my server.js file:
if (process.env.NODE_ENV !== "production") {
require("dotenv").config();
}
const express = require("express");
const app = express();
const cors = require("cors");
const path = require("path");
// Middleware
app.use(express.urlencoded({ limit: "10mb", extended: true }));
app.use(express.json({ limit: "10mb" }));
app.use(cors());
// Connect to Mongoose
const mongoose = require("mongoose");
mongoose.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true
});
const db = mongoose.connection;
db.on("error", err => console.error(err));
db.once("open", () => console.log("Connected to Mongoose"));
// Define routes
const indexRouter = require("./routes/index");
const authorRouter = require("./routes/authors");
const bookRouter = require("./routes/books");
// Utilize routes
app.use("/", indexRouter);
app.use("/authors", authorRouter);
app.use("/books", bookRouter);
app.listen(process.env.PORT || 3000);
Can you try this code, if your app is in production express will serve static assets and it will redirect user to index.html if user comes to routes other than api routes(by providing app.get('*', ...))
server.js
...
app.use("/books", bookRouter);
if(process.env.NODE_ENV === 'production') {
app.use(express.static(path.join(__dirname, '/client/dist')))
app.get('*', (req,res) => {
res.sendFile(path.join(__dirname, '/client/dist', 'index.html'))
})
}
Related
I'm having some issues with my application after it's been deployed to heroku. When I specify the URL, or refresh the browser on a page OTHER than the homepage I am getting an "Internal Server error" and the page doesn't load. However when I click the links which naviagte me to those pages from the home page it works fine. When I run the server locally it does not give me this error.
Based on my research this is probably an error on the server side. Here is my code:
Node.js backend
const express = require("express");
const bodyParser = require("body-parser");
const passport = require("passport");
const users = require("./routes/api/users");
const actions = require("./routes/api/dbActions");
const app = express();
// Bodyparser middleware
app.use(
bodyParser.urlencoded({
extended: false
})
);
app.use(bodyParser.json());
// Passport middleware
app.use(passport.initialize());
// Passport config
require("./config/passport")(passport);
// Routes
app.use("/api/users", users);
app.use("/api/dbActions", actions);
// Serve static assets if in production
if (process.env.NODE_ENV === 'production') {
// Set static folder
app.use(express.static('client/build'));
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
});
}
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`Server running on port ${port} !`));
Any idea why this might be happening?
Thanks!!
In my server.js file I included the following:
const path = require('path');
Which seems to have worked for me.
I want to deploy my app to aws, i search and i found alot of tutorials i try each one and i get this error on the browser:
Cannot GET /
I figure maybe that my problem is from my nodeJS server code.
This is my server.js code hope you guys can help me thanks.
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const passport = require('passport');
const path = require('path');
const cors = require('cors');
//Api routes
const users = require('./routes/api/usuario');
const alumno = require('./routes/api/alumno');
const personal = require('./routes/api/personal');
const zonas = require('./routes/api/zonas');
const sepomex = require('./routes/api/sepomex');
const app = express();
app.use(cors());
//Body parser middleware
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
//Db config
const db = process.env.NODE_ENV === "production" ? require('./config/keys').mongoURIProd : require('./config/keys').mongoURIDev;
//connect to mongo DB
mongoose
.connect(db, { useNewUrlParser: true })
.then(() => console.log('MongoDB Connected'))
.catch(err => console.log(err));
//passport middleware
app.use(passport.initialize());
//passport config
require('./config/passport')(passport);
//Use routes
app.use('/api/usuario', users);
app.use('/api/alumno', alumno);
app.use('/api/personal', personal);
app.use('/api/zonas', zonas);
app.use('/api/sepomex', sepomex);
//serve static assets to production
if (process.env.NODE_ENV === "production") {
//static folder
app.use(express.static("client/build"));
app.get('/*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
})
}
const port = process.env.PORT || 5000;
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
I have deployed my app on heroku and works fine.
If you are deploying to EC2 instance then you need to specify IP address in app.listen to be 0.0.0.0, by default it is set to localhost which is not what you want if you want the app to be reachable from outside.
You should change your code to
app.listen(port, '0.0.0.0', () => {
console.log(`Server running on port ${port}`);
});
My app is running on heroku and the routes send me JSON files, but what I need is to use the SPA that is in my ./server/public folder as index.html.
When I open the heroku app it send me the JSON file that is sent from the "/" route, but I need it to use the Vue SPA, because of what I did in my front-end client if for some reason you go to a route that doesn't exists it does redirect me to my "/forum" route with the front-end client, but any other route that exists will not work, it will keep sending the JSON of that route.
app/server.js
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const expressValidator = require("express-validator");
const flash = require('connect-flash');
const mongoose = require('mongoose');
const cors = require("cors");
const config = require("./config/database");
if(process.env.NODE_ENV !== "production") require('dotenv').config();
mongoose.connect(config.database, { useNewUrlParser: true, useFindAndModify: false });
let db = mongoose.connection;
db.once("open", () => {
console.log("Connected to MongoDB Atlas");
});
db.on("error", (err) => {
console.log(err);
});
const app = express();
//Body-parser
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());
app.use(expressValidator());
app.use(flash());
app.use(cors());
require("./passport")
//Routes
const home = require("./routes/home.js");
const forum = require('./routes/forum.js');
const ranking = require('./routes/ranking.js');
const profile = require('./routes/profile.js');
// Routing
app.use("/", home);
app.use("/forum", forum);
app.use("/profile", profile);
app.use("/ranking", ranking);
// Handle production build
if (process.env.NODE_ENV === "production") {
app.use(express.static(__dirname + '/public/'));
app.get(/.*/, (req, res) => { res.sendFile(__dirname + '/public/index.html') });
}
// PORT
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Server listening on port ${port}`)
});
If you are splitting the frontend and the backend with Vue and Express, you should probably build and host the Vue part statically and then have your Express running on Heroku.
Here is a guide on how to deploy your express app, and then you build and deploy your static Vue page, here are the docs for deploying Vue.
Express routes can not be reached directly without serving the react-app on heroku.
I have developed a react-app that runs with a node.js / express backend on heroku. Everything within the react-app works fine.
Now i need to access an express route (e.g. https://example.com/api/info) that makes a call to a mongo db and should only return some JSON.
The problem is, that express always serves the react-app. From within the react-app i can however access the express routes and use them for e.g. login/signup (https://example.com/api/users/login). I just can't access them in production on heroku directly without serving the react-app.
Since heroku decides the port on which the express app lives i can't add a proxy to the react-app's package.json
The react-app is build in the root>client folder.
Please find below the root index.js file of the node.js / express server
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const passport = require('passport');
const keys = require('./config/keys');
const auth = require('./routes/api/auth');
const mail = require('./routes/api/mail');
const newsletter = require('./routes/api/newsletter');
//connect to mongoDB
mongoose.connect(keys.mongoURI)
.then(() => console.log('MongoDB Connected'))
.catch(err => console.log(err));
const app = express();
// Body parser middleware
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// Passport Config
app.use(passport.initialize());
require('./services/passportJwt')(passport);
// Routes
app.use('/api/users', auth);
app.use('/api/mail', mail);
app.use('/api/newsletter', newsletter);
app.get('/api/info', (req, res) => { //ROUTE DOESN'T WORK, SERVES REACT-APP
res.json({ msg: "Some Info" });
});
// Serve React App in Production
if (process.env.NODE_ENV === 'production') {
// Express will serve up production assets, main.js/main.css
app.use(express.static('client/build'));
// Express will serve up the index.html file if it doesn't recognize the route
const path = require('path');
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
});
}
// Use Heroku port or 5000 when in dev
const PORT = process.env.PORT || 5000;
app.listen(PORT);
Expected result:
When reaching "https://example.com/api/info" a JSON output should be received { "msg": "Some info" }.
Instead the react-app is served and the react-app doesnt recognize the route "api/info" within the react-router.
Thanks in advance for your help.
I'm trying to deploy a MERN app on Azure but when I check my url I got cannot the error cannot get /.
I have tried to start my node server on my local machine on production env and I just have a blank page.
I try to check if there is not a path problem in my code so in server.js I have console.log(path.resolve(__dirname, '../client/build')); on my local it show me the correct path but on azure it result of undefined so I guess on azure I can't use __dirname
My app tree is like this:
|--client
|--build
|--node_modules
|--server
|--server.js
|--package.json
My server.js
const dotenv = require('dotenv');
dotenv.config();
const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const app = express();
const passport = require("passport");
const path = require('path');
const users = require("./routes/api/users");
// Bodyparser middleware
app.use(
bodyParser.urlencoded({
extended: false
})
);
app.use(bodyParser.json());
// DB Config
const db = require("./config/key.js").mongoURI;
console.log(db);
// Connect to MongoDB
mongoose.Promise = global.Promise;
mongoose.connect(db, { useNewUrlParser: true })
.then(() => console.log("MongoDB successfully connected"))
.catch(err => console.log(err));
// Passport middleware
app.use(passport.initialize());
// Passport config
require("./config/passport")(passport);
// Routes
app.use("/api/users", users);
// Serve static assets if in production
if (process.env.NODE_ENV === 'production') {
// Set static fold
console.log(path.resolve(__dirname, '../client/build'));
app.use(__dirname, express.static('../client/build'));
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, '../client', 'build',
'index.html'));
});
}
const port = process.env.PORT || 5000;
app.listen(port, () => console.log('Server up and running on port
${port} !'));
To be honest I don't know what I expect to have.
When I develop on my local I have my node-js server started and my react server started too.
When I try to make some search about how to deploy I can only find tutorial which explain 'copy paste this and it will work' but I have no idea how it work ?
With all my search, I guess I have to run the node server and it will render my front-end from the index.html from my build repository then why on azure I got cannot get / ?
##EDIT##
Actually the problem was Azure doesn't start my server.js in production mode so my condition was never hit.
I don't have this cannot get / any more and I have the same problem in local, I have got a blank page. If I open the inspector I can see it's my index.html and I can in the console I have this error
Uncaught SyntaxError: Unexpected token < main.a3362a52.chunk.js:1
Uncaught SyntaxError: Unexpected token < index.bundle.js:1
Could it be the cause of the blank page ?