HPM Error occurred while trying to proxy request in Heroku - node.js

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!

Related

Network error React(frontend) and Nodejs(backend)

I am unable to link my frontend(React) and backend(Nodejs) together with Axios as it keeps encountering Network Error (I have also included CORS into my backend application but it still does not work). The backend runs on Postman too but not when integrated with the frontend. Frontend is running at localhost:3000 while backend runs at localhost:5000
Error: Network Error
at createError (createError.js:16)
at XMLHttpRequest.handleError (xhr.js:83)
Here is what my code looks like:
Frontend:
axios.get("http://localhost:5000")
.then((res) => {
console.log(res.data);
alert("Hi succeedd");
})
.catch((err) => {
console.error(err);
alert("Try again");
});
Backend:
const express = require("express");
const connectDB = require("./config/db");
var cors = require("cors");
const app = express();
app.use(cors({ origin: true, credentials: true }));
//Connect to database
connectDB();
//Init middleware to read data sent in req.body
app.use(express.json({ extended: false }));
app.get("/", (req, res) => res.send("API running"));
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on ${PORT}`));
have you set up proxy in your package.json correctly?
you should add this line: "proxy": "http://localhost:5000" so that the it knows to proxy requests between a local frontend and backend
see this for more details: https://www.twilio.com/blog/react-app-with-node-js-server-proxy

Why is my MEVN app only showing the backend when deploying to Heroku?

My app is currently deployed online through Heroku but it is displaying the backend server rather than my Vue app.
Note: I have an if statement in app.js that serves the files only in production. I removed the if statement to see if the app would appear without environmental conditions. It did not work.
Also I have a minified Vue js folder called dist in my express directory.
Updated
The heroku method I am using is the Heroku Git Cli
$ cd my-project/
$ git init
$ heroku git:remote -a testingew
$ git add .
$ git commit -am "make it better"
$ git push heroku master
This is what I see, which is the backend response to the "/" route. The code is in app.js
app.js
The full code
const serveStatic = require("serve-static");
const path = require("path");
const express = require("express");
const app = express();
const userRoutes = require("./routes/user");
const budgetRoutes = require("./routes/budget");
const profileRoutes = require("./routes/profile");
require("dotenv/config");
const port = process.env.PORT || 3000;
const cors = require("cors");
const morgan = require("morgan");
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
const cookieParser = require("cookie-parser");
const sessions = require("express-session");
const MongoStore = require("connect-mongo")(sessions);
const passport = require("passport");
const passportSetup = require("./config/passport.js");
//Log when making request
app.use(morgan("combined"));
//Parse body for post request
app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
var corsOption = {
origin: true,
methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
credentials: true
};
app.use(cors(corsOption));
app.use(
sessions({
secret: process.env.SESSION_COOKIEKEY,
saveUninitialized: false,
resave: false,
store: new MongoStore({
mongooseConnection: mongoose.connection
}),
cookie: {
// secure: true,
maxAge: 86400000
}
})
);
//Initialize passport
app.use(passport.initialize());
app.use(passport.session());
//Automatic route placer
app.use("/auth", userRoutes);
app.use("/api/budget", budgetRoutes);
app.use("/api/profile", profileRoutes);
app.get("/", (req, res) => {
res.send("App is on");
});
//404 error and pas to error handler
app.use((req, res, next) => {
const error = new Error("An error has occured");
error.status = 404;
next(error);
});
//Error handler
app.use((error, req, res, next) => {
//Respond Client
res.status(error.status || 500);
res.json({
error: {
message: error.message
}
});
console.log(error.message);
});
//Mongo connection
mongoose.connect(
process.env.DB_CONNECTION,
{
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false
},
() => console.log("connected to mongo atlas")
);
//Handle production
app.use(express.static(path.join(__dirname, "dist")));
// Redirect all requests to `index.html`
app.get("/*", (req, res) => {
res.sendFile(path.join(__dirname, "dist", "index.html"));
});
//Start app
app.listen(port, () => {
console.log(`Server is on port ${port}`);
});
Vue Router
Note: I am also using route navigation guards within components if that means anything
import Vue from "vue";
import VueRouter from "vue-router";
//import axios from "axios";
Vue.use(VueRouter);
const routes = [
{
path: "/",
name: "login",
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "about" */ "../views/Login.vue")
},
{
path: "/userprofile",
name: "userProfile",
component: () => import("../views/userProfile.vue"),
meta: {
requireAuth: true
}
},
{
path: "/budgetform",
name: "form",
component: () => import("../views/budgetForm.vue"),
meta: {
requireAuth: true
}
},
{
path: "/mybudget",
name: "myBudget",
component: () => import("../views/myBudget.vue"),
meta: {
requireAuth: true
}
}
];
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes
});
export default router;
File directory of Express
Hopefully this helps
EDIT
Based on your edit showing your Express app, you need to remove everything between
app.use("/api/profile", profileRoutes);
and Mongo. The app can't run if you do other things on the route instead. The "App is on" and error checking stuff is blocking your app. I don't think you mean to run the error on every route, but that's what's happening. I would suggest browsing some Express tutorials and starting from scratch to try to understand why that can't work.
ORIGINAL
Try to replace everything above app.listen with:
// Serve static assets
app.use(express.static(path.join(__dirname, 'dist')));
// Redirect all requests to `index.html`
app.get('/*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist', 'index.html'));
})
Your wildcard match was wrong, and would only match file paths that begin with a dot, path.join wasn't used correctly, there should be no slash literals. I changed some other syntax too. If this doesn't completely fix the issue, there are many possibilities for error that are beyond the scope of this question:
1) directory structure
2) .gitignore
3) vue-router
4) package.json start script

Deploying an Angular Build on Nginx for Windows

I'm trying to deploy my Angular Project's build (which also has a REST API in it) on nginx, but the localhost refuses to connect when I load the Dist inside the html folder.
A localhost run using npm run build does listen to it, but I cannon seem to crack the code on how to deploy that exact output onto the Nginx webserver.
(Inside my Angular Project)
server.js
// Get dependencies
const express = require('express');
const path = require('path');
const http = require('http');
const bodyParser = require('body-parser');
// Get our API routes
const api = require('./server/routes/api');
const app = express();
// Parsers for POST data
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// Point static path to dist
app.use(express.static(path.join(__dirname, 'dist')));
// Set our api routes
app.use('/api', api);
// Catch all other routes and return the index file
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist/ProjectName/index.html'));
});
/**
* Get port from environment and store in Express.
*/
const port = process.env.PORT || '3000';
app.set('port', port);
/**
* Create HTTP server.
*/
const server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port, () => console.log(`API running on localhost:${port}`));
server/routes/api (for testing)
const express = require('express');
const router = express.Router();
// declare axios for making http requests
const axios = require('axios');
const API = 'https://jsonplaceholder.typicode.com';
/* GET api listing. */
router.get('/', (req, res) => {
res.send('api works');
});
// Get all posts
router.get('/posts', (req, res) => {
// Get posts from the mock api
// This should ideally be replaced with a service that connects to MongoDB
axios.get(`${API}/posts`)
.then(posts => {
res.status(200).json(posts.data);
})
.catch(error => {
res.status(500).send(error)
});
});
module.exports = router;
package.json script
"scripts": {
"ng": "ng",
"start": "ng serve --proxy-config proxyConfig.json",
"build": "ng build && node server.js",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
}
proxyConfig.json
{
"/api": {
"target": "http://localhost:3000",
"secure": false,
"changeOrigin": true
}
}
nginx.conf
server {
listen 8080;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html/dist/ProjectName;
index index.html index.htm;
}
location /api {
proxy_pass http://localhost:3000;
Replace the line
app.use(express.static(path.join(__dirname, 'dist')));
with
app.use(express.static(path.join(__dirname, 'dist/ProjectName')));

Vue + Express app on heroku not using the SPA in server/public folder

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.

MERN App Deployed to Heroku Not Working Properly

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...');
};

Resources