I have a NodeJS and React project. I am having a problem. I didnt understand what the problems are. First I want to show you what my problem is.
on localhost. I want like this on heroku but it returns me as the next one
My problem's photo:
As you see on the photo there is an html file in my user object on Heroku deployment.
But when I start my project on localhost the user has an object that is coming from my mongo database. Express.json() is catching and working correctly on localhost but it doesnt work on Heroku. Why does it happen? What is my problem?
Here is my server.js file :
const express = require("express");
const path = require("path");
const app = express();
const connectDB = require("./config/db");
const PORT = process.env.PORT || 5000;
connectDB();
app.use(express.json());
app.use(express.json({ extended: false }));
app.use(express.static("client/build"));
if (process.env.NODE_ENV === "production") {
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname, "client", "build", "index.html"));
});
}
app.use("/", require("./routes/quizRoute"));
app.use("/users", require("./routes/userRoute"));
app.use("/auth", require("./routes/authRoute"));
app.listen(PORT, () => {
console.log("Server is started on the port " + PORT);
});
Client Side request
//SET AUTH WORK
import axios from "axios";
const setAuthToken = token => {
if (token) {
axios.defaults.headers.common["x-auth-token"] = token;
} else {
delete axios.dafaults.header.common["x-auth-token"];
}
};
export default setAuthToken;
//LOAD USER
const loadUser = async () => {
if (localStorage.token) {
setAuthToken(localStorage.token);
}
try {
const res = await axios.get("/auth");
dispatch({ type: USER_LOADED, payload: res.data });
} catch (err) {
dispatch({
type: AUTH_ERROR,
payload:
"AUTH_ERROR: Token dogrulanamadi veya /auth'a GET isteginde bir sorun var"
});
}
};
enter code here
Why you are having the issue:
I believe your bundler sets the NODE_ENV value to production when it builds for the deploy environments i.e Heroku. So you are having this issue because of the catch-all route that sends back your client index.html on every get request:
if (process.env.NODE_ENV === "production") {
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname, "client", "build", "index.html"));
});
}
Consequently, when you make the get request to the /auth route from your client app, the request gets intercepted by this catch-all route handler before getting to the expected route handler. I'm pretty certain you would get back that HTML string whenever you make any get request to your server, not just the for /auth
The solution:
The easy fix for this would be for you to move the catch-all route below your API routes like this:
app.use("/", require("./routes/quizRoute"));
app.use("/users", require("./routes/userRoute"));
app.use("/auth", require("./routes/authRoute"));
// Every other API or similar routes should be before this catch-all
if (process.env.NODE_ENV === "production") {
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname, "client", "build", "index.html"));
});
}
Related
I have deployed my mern app on heroku. My front end is being displayed, but I can't access my database, probably my server is causing issue.
My server code
const port = process.env.PORT || 5000;
// Database Connection
const CONNECTION_URL = "mongodb+srv://Toor_300:ARh3SwhrBl5Nvaur#cluster0.rudek.mongodb.net/courses_site"
mongoose.connect(CONNECTION_URL, { useNewUrlParser:true })
.then(() => app.listen(port, () => console.log('Listening to Port')))
.catch((err) => console.log(`Connection Error: ${error}`))
// Middlewares
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
app.use(express.urlencoded({ extended: true }))
app.use(cors())
// Heroku
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'))
})
}
// Routes
app.use("/register", userRoute)
app.use("/admin", adminRoute)
My Axios Api in React
import axios from 'axios'
const CONNECTION_URL = "http://localhost:5000/admin"
export const createUser = (user) => axios.post(CONNECTION_URL, user)
export const getUser = (user) => axios.get(`${CONNECTION_URL}/login/${user.email}&${user.password}`)
export const resetPassword = (email) => axios.get(`${CONNECTION_URL}/reset/${email}`)
export const updatePassword = (info) => axios.patch(`${CONNECTION_URL}/reset`, info)
I have tried many options but it didn't seem to work. I think the error is with my axios. The url provided is local how do I make it dynamic?
https://coursework.vschool.io/deploying-mern-app-to-heroku/
I'm always following this blog to deploy the MERN app you have to mainly focus on directory structure and package.json setup on the server side
I am working on a MERN website where I'm using React-router and it works fine while I'm clicking on link buttons, but when I refresh my webpage it does not load what I want. React router URL doesn't work when refreshing or writing manually. so I saw many solutions for that problem. I used one solution that is to catch every get request in server.js and send index.html file.
if (process.env.NODE_ENV === "production") {
app.use(express.static(path.join(__dirname, "client", "build")));
app.get("/*", function (req, res) {
res.sendFile(path.join(__dirname, "client", "build", "index.html"));
});
}
This process worked perfectly and solved the react-router problem but on the client-side, I was getting some data by sending Axios get request.
useEffect(() => {
axios
.get("/posts")
.then((result) => {
console.log(result.data);
//result.data contains index.html file instead of json data
})
.catch((error) => {
console.log(error);
});
}, []);
the response of that get request should have get JSON data but it gets index.html file. I know it gets that index.html file because I send the index.html file when catching all requests in server.js.
app.get("/*", function (req, res) {
res.sendFile(path.join(__dirname, "client", "build", "index.html"));
});
but if I remove that line of code then I face React router URL doesn't work error when refreshing or writing URL manually.
what I want is to solve the react-router problem and also get JSON data from that request and use that data to map through those data. Here are my server.js, App.js, and Posts.js code
server.js
const express = require("express");
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
require("dotenv").config();
const path = require("path");
const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
// parse application/json
app.use(bodyParser.json());
if (process.env.NODE_ENV === "production") {
app.use(express.static(path.join(__dirname, "client", "build")));
app.get("/*", function (req, res) {
res.sendFile(path.join(__dirname, "client", "build", "index.html"));
});
}
mongoose
.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: false,
})
.then(() => {
console.log("Database connected");
})
.catch((err) => {
console.log(`Database error ${err.message}`);
});
const postsSchema = new mongoose.Schema({
title: {
type: String,
},
description: {
type: String,
},
});
const Posts = new mongoose.model("Posts", postsSchema);
app.get("/posts", (req, res) => {
Posts.find({}, (error, post) => {
if (error) {
res.json(error.message);
} else {
res.json(post);
}
});
});
app.listen(process.env.PORT || 5000, () => {
console.log("App started in port 5000");
});
App.js
import "./App.css";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import Posts from "./Posts";
import Header from "./Header";
function App() {
return (
<div className="App">
<Router>
<Header />
<Switch>
<Route exact path="/posts" component={Posts} />
</Switch>
</Router>
</div>
);
}
export default App;
Posts.js
import React, { useState, useEffect } from "react";
import axios from "axios";
import { Link } from "react-router-dom";
function Posts() {
const [posts, setPosts] = useState([]);
useEffect(() => {
axios
.get("/posts")
.then((result) => {
setPosts(result.data);
console.log(result.data);
//result.data contains index.html file instead of JSON data
})
.catch((error) => {
console.log(error);
});
}, []);
return (
<div className="container mt-5">
<div className="row">
{posts.map((post) => {
return (
<div className="col-sm-3" key={post._id}>
<h6>{post.title}</h6>
<p>{post.description}</p>
<Link className="nav-link active" to={`/post/${post._id}`}>
Read More
</Link>
</div>
);
})}
</div>
</div>
);
}
export default Posts;
Please help me if you know what is the problem with my code. and how can I get JSON data instead of that index.html file
const express = require("express");
//const bodyParser = require("body-parser"); not necessary
const mongoose = require("mongoose");
require("dotenv").config();
const path = require("path");
const app = express();
//app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.urlencoded({ extended: false }));
//app.use(bodyParser.json());
app.use(express.json());
/*if (process.env.NODE_ENV === "production") {
app.use(express.static(path.join(__dirname, "client", "build")));
app.get("/*", function (req, res) {
res.sendFile(path.join(__dirname, "client", "build", "index.html"));
});
}*/
mongoose
.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: false,
})
.then(() => {
console.log("Database connected");
})
.catch((err) => {
console.log(`Database error ${err.message}`);
});
const postsSchema = new mongoose.Schema({
title: {
type: String,
},
description: {
type: String,
},
});
const Posts = new mongoose.model("Posts", postsSchema);
// Static Files Folder
app.use(express.static(path.join(__dirname,"client","bluild")))
// Routes
app.use("/posts", (req, res)=>{
Posts.find({}, (error, post) => {
if (error) {
res.json(error.message);
} else {
res.json(post);
}
});
})
app.use("/*", (req, res)=>{res.sendFile(path.join(__dirname,"public/index.html"))}) /*always at the end*/
app.listen(process.env.PORT || 5000, () => {
console.log("App started in port 5000");
});
That should work, now I'd like to give you some tips.
Express in last versions include body-parser
If you are going to use 1 server that handles client-side + server-side (i mean without using create-react-app and creating other server to make requests):
Fist, you have to declear the static folder where you gonna put you bundle made with react (in your case, I think it's index.js).
It's not necessary to re-send the bundle for a route that does not exist, you can send a json to the client-side (res.json({Status: 'NotFound'})) and there you'll have to show to user that page don't exist (using react, create a component NotFound and make sure it's the last route in the routing) or just send res.status(401). Though, this isn't necessary if you use the next point.
In react-router, you should use "HashRouter as Router" instead of "BrowserRouter as Router", it's a little bit dificult to explain you can find out about it.
I'm new to frontend development and express server. When I tried to start an express.js server with react (with axios calls to external apis), it seems express.js is adding 'localhost:3000' in front of the external API calls so they fail.
In my server.js:
const path = require('path');
const express = require('express');
const app = express();
const publicPath = path.join(__dirname, '.', 'dist');
const port = process.env.PORT || 3000;
app.use(express.static(publicPath));
app.get('*', (req, res) => {
res.sendFile(path.join(publicPath, 'index.html'));
});
app.listen(port, () => {
console.log('Server is up!');
});
Which leads to the API call to www.example.com/api/ to become http://localhost:3000/www.example.com/api/
I also tried to filter the req by writing:
app.get('*', (req, res) => {
if (req.url.match(/\/api\//) === null) {
res.sendFile(path.join(publicPath, 'index.html'));
}
});
But it does not change things...
Can anyone help out this newbie that is me?
Update1 Adding the code for calling the api:
This is the api call:
const getSomething = () => {
try {
const url = endpoints.GET_SOMETHING;
return axios.get(url);
} catch (err) {
console.log(err);
}
};
endpoints.GET_SOMETHING is the api URL: www.example.com/api/getSomething
You need to put a / in the url
app.get('/*', (req, res) => {
res.sendFile(path.join(publicPath, 'index.html'));
});
and also your endpoint url should start with https://, http:// or //
Everything works fine when I work on localhost:8000 but when I deployed it to heroku all the routes are not working
Here is my server.js:
const express = require("express"),
app = express(),
cors = require("cors"),
port = process.env.PORT || 8000,
db = "beuter",
path = require("path"),
server = app.listen(port, () => console.log(`Listening to on port ${port}`));
app.use(cors());
app.use(express.json());
if (process.env.NODE_ENV === "production") {
app.use(express.static('beuter/build'))
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'beuter', 'build', 'index.html'));
})
}
require("./server/config/database.config")(db);
require("./server/routes/product.route")(app);
and here is my server/routes/product.route.js:
const product = require("../controllers/product.controller");
var path = require("path");
module.exports = (app) => {
app.get("/api/products", product.index);
// Create a product
app.post("/api/products", product.create);
// Get one product by title_url
app.get("/api/products/:title_url", product.show_title_url)
// Delete a product
app.delete("/api/products/:id", product.deleteProduct)
//Edit a product
app.put("/api/products/:id", product.update)
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'beuter', 'build', 'index.html'));
})
};
This is the error in my chrome console
Access to XMLHttpRequest at 'localhost:8000/api/products' from origin 'https://thebeuter.herokuapp.com' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.
Here is the github project if you want to look over my code:
https://github.com/nathannewyen/the-beuter
Updated:
Here is my Form.jsx from front-end:
const addProduct = (e) => {
e.preventDefault();
const product = {
title,
title_url,
price,
description1,
description2,
description3,
description4,
description5,
img_url1,
img_url2,
img_url3,
img_url4,
size,
size2,
fit,
fit2,
category,
};
axios
.post("https://localhost:8000/api/products", product)
.then((res) => {
if (res.data.errors) {
setErrors(res.data.errors);
} else {
navigate("/");
}
})
.catch((err) => console.log(err));
};
I have been frantically trying for hours to get my email working.
This is the website:https://www.shafirpl.com/contact
I have a react app hosted on the same server ( a digital ocean droplet) as node.js app. The domain name(shafirpl.com) has SSL certificate from cloudflare. The node.js app is running on port 4000 while the react app on port 80. So what is happening now is that the react production build is running on port 80 of that IP address/server, and I have an axios post request when the user clicks the send button. When it was on my local machine it worked as the axios request was using "http://localhost:4000/email". But when I deployed on the server and changed the URL to "http://myServerIpAddress:4000/email" I get the error that says I have to send the request via https. I am not sure how to generate an SSL certificate so that my front end react app can commit the axios request and don't have the issue. I have tried to follow certbot tutorial but it seems like certbot requires a specific domain name. SO what I did is that I created key-cert pairs for my domain name (shafirpl.com) using this tutorial (https://dev.to/omergulen/step-by-step-node-express-ssl-certificate-run-https-server-from-scratch-in-5-steps-5b87) and am using in my server.js file (the node.js app brain) like this:
const express = require("express");
// const connectDB = require("./config/db");
const path = require("path");
const https = require("https");
const fs = require("fs");
// routes variables
const emailRoute = require("./routes/email");
const resumeRoute = require("./routes/resume");
// const authRoute = require("./routes/api/auth");
const app = express();
var cors = require("cors");
// var corsOptions = {
// origin: "*",
// optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
// };
app.use(cors());
app.options("*", cors());
// Connect Database
// connectDB();
// Middleware initialization
/*
* Usually we used to install body parser and do
* app.use(bodyparser.json()). But now bodyparser comes
* packaged with express. So we just have to do express.json()
* to use bodyparser
*/
app.use(express.json({ extended: false }));
// use this when on my pc
// app.use(function (req, res, next) {
// res.header("Access-Control-Allow-Origin", "http://localhost:3000"); // update to match the domain you will make the request from
// res.header(
// "Access-Control-Allow-Headers",
// "Origin, X-Requested-With, Content-Type, Accept"
// );
// next();
// });
// use this on produnction
// app.use(function (req, res, next) {
// res.header("Access-Control-Allow-Origin", "*"); // update to match the domain you will make the request from
// res.header(
// "Access-Control-Allow-Headers",
// "Origin, X-Requested-With, Content-Type, Accept"
// );
// next();
// });
// app.get("/", (req,res) => {res.send('API Running')});
// Define Routes
app.get("/", (req, res) => {
res.send("Server Running");
});
app.use("/email", emailRoute);
app.use("/resume", resumeRoute);
// app.use("/api/auth", authRoute);
// app.use("/api/profile", profileRoute);
// app.use("/api/posts", postsRoute);
// // serve static assets 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"));
// });
// }
/*
* This means when the app will be deployed to heroku, it will
* look for a port specified by heroku. But since right now
* locally we don't have that, we will be running the app on
* port 5000
*/
// const PORT = process.env.PORT || 4000;
// app.listen(PORT, () => {
// console.log(`Server started on port ${PORT}`);
// });
app.listen(4000);
// comment out this line when testing on localhost
const httpsServer = https.createServer(
{
key: fs.readFileSync("/etc/letsencrypt/live/shafirpl.com/privkey.pem"),
cert: fs.readFileSync("/etc/letsencrypt/live/shafirpl.com/fullchain.pem"),
},
app
);
httpsServer.listen(443, () => {
console.log("HTTPS Server running on port 443");
});
And in my axios.post I am using like this
const url = "https://shafirpl.com:443/email";
const sendMessage = async () => {
const config = {
headers: {
'Content-Type': 'application/json',
}
}
const body = JSON.stringify({ name, email, company, message });
try {
const res = await axios.post(url, body, config);
console.log(res);
clearForm();
showSuccessMessage();
} catch (error) {
console.log(error);
showFailureMessage();
}
}
const showFailureMessage = () => {
setFailureAlert(true);
setTimeout(() => {
setFailureAlert(false)
}, 3000);
}
But right now I am again getting this error:
Access to XMLHttpRequest at 'https://shafirpl.com/email' from origin 'https://www.shafirpl.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I actually don't know how to solve this as I am pretty new to the total MERN stack build. Can anyone help me with this? I just wanna send email using the axios
I had the same issue - what I did, I removed explicit ports from both server and client. Then I noticed that I was hitting http://mydomain.... please try accessing it from https://mydomain... that did the trick for me :) Hope it helps!
I think i fixed the issue. Instead of running 2 different application, I decided to serve my react build via my node.js app. The way I did was this:
const express = require("express");
// const connectDB = require("./config/db");
const path = require("path");
// routes variables
const emailRoute = require("./routes/email");
const resumeRoute = require("./routes/resume");
const app = express();
app.use(express.json({ extended: false }));
app.use("/api/email", emailRoute);
app.use("/api/resume", resumeRoute);
app.use(express.static("client/build"));
app.get("*", (req, res) => {
res.sendFile(path.resolve(__dirname, "client", "build", "index.html"));
});
app.listen(80);
Then on my axios request I just did that:
const url = "/api/email"; const sendMessage = async () => {
const config = {
headers: {
'Content-Type': 'application/json',
}
}
const body = JSON.stringify({ name, email, company, message });
try {
const res = await axios.post(url, body, config);
console.log(res);
clearForm();
showSuccessMessage();
} catch (error) {
console.log(error);
showFailureMessage();
}
}
Right now everything is working fine.
For the resume thing which sends a file download, instead of using /api/resume I had to do
something like this
<Nav.Link eventKey="6" activeClassName="active-nav" href="https://shafirpl.com/api/resume" target="_blank" rel="noopener noreferrer">Resume</Nav.Link>
And right now the resume download is also working fine
Thanks for all the help