I've deployed a MERN app to Heroku. When I go to the app, I'm able to post data through my APIs to the MongoDB database, however, whenever I make a GET request, Heroku responds with:
at=info method=GET path="/api/lists/5b44001a558fe30014e8c43c" host=bootcamp-bucket-list.herokuapp.com request_id=e9b06431-aa30-4811-bf7d-a46720991646 fwd="24.124.88.220" dyno=web.1 connect=0ms service=2ms status=304 bytes=237 protocol=https
I am able to run the app locally on my servers without any issues, it's just when we're in prodution, the GET requests fail. Has anyone experienced this before and know what could be causing this issue? Let me know if any additional info is needed.
Here is the setup of my server.js file:
const express = require('express');
const path = require('path');
const users = require('./routes/api/users');
const routes = require('./routes');
const app = express();
const port = process.env.PORT || 5001;
const bodyParser = require("body-parser");
const passport = require('passport');
const mongoose = require("mongoose");
const Models = require('./models');
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());
mongoose.Promise = Promise;
var MONGODB_URI = process.env.MONGODB_URI || "mongodb://localhost/testdb";
console.log(MONGODB_URI);
mongoose.connect(MONGODB_URI);
const db = mongoose.connection;
app.use(passport.initialize());
// PASSPORT CONFIG
require('./config/passport')(passport);
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, './client/build/index.html'));
});
if (process.env.NODE_ENV === 'production') {
// Serve any static files
app.use(express.static('client/build'));
}
// USE ROUTES
app.use('/api/users', users);
app.use(routes);
app.listen(port, () => console.log(`Listening on port ${port}`));
I also have the following scripts in my package.json file:
"start": "node server.js",
"heroku-postbuild": "NPM_CONFIG_PRODUCTION=false npm install --prefix client && npm run build --prefix client"
You need to make sure that when running in production mode that you are reserving your api endpoint.
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, './client/build/index.html'));
});
should be something like
if (process.env.NODE_ENV === 'production') {
app.use(express.static('client/build')); // serve the static react app
app.get(/^\/(?!api).*/, (req, res) => { // don't serve api routes to react app
res.sendFile(path.join(__dirname, './client/build/index.html'));
});
console.log('Serving React App...');
};
Related
I get this error: [HPM] Error occurred while trying to proxy request /api/artefact from myapp.herokuapp.com to http://localhost:5000 (ECONNREFUSED) when I deploy my MERN app.
These are the relevant files in terms of proxying and deploying:
setupProxy.js
const proxy = require("http-proxy-middleware");
module.exports = app => {
app.use(proxy("/api/", { target: "http://localhost:5000" }));
};
server.js
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const logger = require("morgan");
const { mongo_uri } = require("./config/config");
const path = require("path");
let cors = require("cors");
const PORT = process.env.PORT || 5000;
const app = express();
app.use(cors());
// Set up Routes
const artefactRoutes = require("./routes/api/artefact.js");
const userRoutes = require("./routes/api/users.js");
const authRoutes = require("./routes/api/auth.js");
// Connect to MongoDB Database
const dbRoute = mongo_uri;
mongoose
.connect(dbRoute, { useNewUrlParser: true })
.then(() => console.log("Connected to Database"))
.catch(err => console.log(err));
// Body Parser Middleware to parse request body into readable json format
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// Used for logging
app.use(logger("dev"));
app.use("/api", artefactRoutes);
app.use("/api/users", userRoutes);
app.use("/api/auth", authRoutes);
if (process.env.NODE_ENV === "production") {
app.use(express.static(path.join(__dirname, "../client/build")));
app.get("*", (req, res) => {
// relative path
res.sendFile(path.join(__dirname, "../client", "build", "index.html"));
});
}
app.listen(PORT, () =>
console.log(`Static server has started. Listening on port ${PORT}`)
);
static.json (though I'm not sure really what this does as I just found someone do this on another thread but it didn't change anything for me)
{
"root": "build/",
"clean_urls": false,
"routes": {
"/**": "index.html"
}
}
and my scripts in the parent directory package.json
"scripts": {
"start": "concurrently \"cd backend && node server.js\" \"cd client && npm start\"",
"format": "prettier",
"heroku-postbuild": "cd client && npm install && npm run build"
},
and it might help to mention that my folder structure is like:
-/project
--/client
--/backend
Basically the problem I'm having is that I've deployed my react app to heroku and the frontend loads fine, but none of the axios api calls to my backend are returning anything and just give me a 504 timeout error, and in the heroku logs it just says the proxy error. Any help would be much appreciated!
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.
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 building a web app using Angular2, to create the project I'm using Angular2 CLI webpack. Angular2 app uses other external packages also (Eg: Firebase). In addition to that, I need to create a REST API running on node.js
How can I serve both of Angular2 app and REST API using node.js server
Use ng build to build your app into build directory.
Create nodejs app to server the build directory as static content, then create route for api.
Following is an example of nodejs app using express that will serve the Angular2 app:
/*
Put content of angular2 build into 'public' folder.
*/
const html = __dirname + '/public';
const port = 4000;
const apiUrl = '/api';
// Express
const bodyParser = require('body-parser');
const compression = require('compression');
const express = require('express');
var app = express();
app
.use(compression())
.use(bodyParser.json())
// Static content
.use(express.static(html))
// Default route
.use(function(req, res) {
res.sendFile(html + 'index.html');
})
// Start server
.listen(port, function () {
console.log('Port: ' + port);
console.log('Html: ' + html);
});
// continue with api code below ...
None of the answers worked properly for me. And if it worked, the Angular routing did not work on reload.
So this is how I solved it. Angular routing works even on full page reload.
function getRoot(request, response) {
response.sendFile(path.resolve('./public/angular/index.html'));
}
function getUndefined(request, response) {
response.sendFile(path.resolve('./public/angular/index.html'));
}
// Note the dot at the beginning of the path
app.use(express.static('./public/angular'));
app.get('/', getRoot);
app.get('/*', getUndefined);
NO angular base-href rewrite is required! Just use ng build or ng build --prod.
Here is full back end code which is working
const express = require('express');
var app = express();
var port = 9999;
function getRoot(request, response) {
response.sendFile(path.resolve('./public/angular/index.html'));
}
function getUndefined(request, response) {
response.sendFile(path.resolve('./public/angular/index.html'));
}
app.use(express.static('./public/angular'));
app.get('/', getRoot);
app.get('/*', getUndefined);
// Start server
app.listen(port, function () {
console.log('server running at port: ' + port);
});
Based on #NTN-JAVA answer, here's a solution to serve an Angular app from NodeJS server.
Here's the summary from beginning:
npm install -g #angular/cli
ng new PROJECT_NAME
cd PROJECT_NAME
npm install nodemon express cookie-parser body-parser morgan method-override --save
5.Create app.js:
var express = require('express');
var app = express();
var morgan = require('morgan');
var bodyParser = require('body-parser');
var port = process.env.PORT || 3000;
var methodOverride = require('method-override'); // simulate DELETE and PUT (express4)
var router = express.Router();
console.log('——————————- Run on port '+ port);
/****************************** Router ***************************/
router.get('*', function(req, res){
res.sendFile('index.html', { root: __dirname + '/' });
});
/****************************** /Router ***************************/
//app.use(morgan('dev')); // log every request to the console
app.use(express.static(__dirname + '/')); // Static (public) folder
app.use(bodyParser.urlencoded({extended:true}));// get information from html forms
app.use(bodyParser.json());
app.use(bodyParser.json({ type: 'application/vnd.api+json' }));
app.use(methodOverride());
app.use('/', router); // app.use('/parent', router); call all from localhost:port/parent/*
app.listen(port);
Edit package.json file:
{
...
"scripts": {
"start": "ng build; cp app.js dist/app.js; node dist/app.js",
}
...
}
Run npm start
This answer also offers a solution for calling direct URLs from browser and resolving them correctly in your app.
Follow the Express node server with Angular 2 CLI document to serve your application through Node.js server. The application is being served Through Node.js and a REST full API. You can design this REST as your requirements.
E.g.
Serve application with http://localhost:5000/app
app.get('/app/*', function(req, res) {
res.sendFile(path.join(__dirname, 'index.html'))
});
or
Serve data from REST calls with http://localhost:5000/rest/contacts
app.get('/rest/user', function(req, res) {
res.send({
"id": 2,
"name": "Jhon",
})
});
Step 1: In order to get static content, run this command in your angular app directory -
ng build --prod
Step 2: The first step will create a dist folder in your current directory, move all files in the dist folder to public folder of your node app -
Step 3: Create a node server. App.js -
var path = require('path');
var express = require('express');
var cookieParser = require('cookie-parser');
var cookieParser = require('cookie-parser');
const allowedExt = [
'.js',
'.ico',
'.css',
'.png',
'.jpg',
'.woff2',
'.woff',
'.ttf',
'.svg',
];
var app = express();
app.use(cookieParser());
function getAngularApp(request, response) {
response.sendFile(path.resolve('./public/index.html'));
}
function defaultHandler(request, response) {
if (allowedExt.filter(ext => req.url.indexOf(ext) > 0).length > 0) {
response.sendFile(path.resolve(`public/${req.url}`));
} else {
response.sendFile(path.resolve('./public/index.html'));
}
}
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.get('/', getAngularApp);
app.get('/*', defaultHandler);
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;