I am using an express server to serve a create-react-app build.
server.js
// Express only serves static assets in production
if (process.env.NODE_ENV === 'production') {
server.use(express.static('client/build')); // <--- this might be causing my problem, how to make work?
}
var api = require('./routes/api');
var email = require('./routes/email');
// Define API routes before the *
server.use('/api', api);
server.use('/email', email);
server.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
});
app.js
class App extends Component {
render() {
return (
<Router history={browserHistory}>
<Route component={Container}>
<Route path="/" component={Main} />
<Route path="/catalog" component={Catalog} />
<Route path="/product/:productId" component={ProductPage} />
<Route path="/faq" component={Faq} />
<Route path="/blog" component={Blog} />
<Route path="/inquiry" component={Inquiry} />
<Route path="/cart" component={Cart} />
</Route>
</Router>
);
}
}
export default App;
Edit:
Updated my server.js file to show relevant code. I think the issue lies in using express.static('client/build'), but I'm not quite sure what to change it to in order to fix my routing issues.
/Edit
This works perfectly fine in Dev and Heroku where clicking links in the app go from route to route. For example being at the root, I can click on a link to "https://thesite.com/catalog" and the app navigates there as with any other route.
However, if I were to copy the URL above and paste it into a new tab/browser, I get a 500 Internal Server Error. This would mean the user cannot bookmark any page other than the root. Or if another site linked to a specific route on my page it would show a 500 error. Refreshing in a page that isn't "/" returns a 500 error. Does anyone know how to fix this?
Figured it out. I needed to add a universal route getter for express.static('client/build').
if (process.env.NODE_ENV === 'production') {
server.use(express.static('client/build'));
server.use('*', express.static('client/build')); // Added this
}
Related
I deployed my MERN stack app to heroku but the router paths dont work when i manually type in the url paths.
This is my node server:
const express = require('express');
const path = require('path');
const connectDB = require('./config/db');
const app = express();
// //connect DB
connectDB();
app.use('/api/poll', require('./routes/api/poll'));
if(process.env.NODE_ENV === 'production'){
app.use(express.static( 'client/build' ));
app.get('*', () => {
res.sendFile(path.join(__dirname, 'client', 'build', 'index.html')); //relative path
});
}
const PORT = process.env.PORT || 5000
app.listen(PORT, () => console.log(`Server started ${PORT}`));
This is the router in react:
<Router>
<Fragment>
<Navbar />
<Switch>
<Route exact path="/" component={Landing} />
<Route exact path="/create" component={CreatePoll} />
<Route exact path="/search" component={Search} />
<Route exact path="/404" component={NoMatch} />
<Route exact path="/:id" component={Poll} />
<Route exact path="/:id/results" component={Results} />
</Switch>
<NotificationContainer />
</Fragment>
</Router>
Basically if I type in a pathname in my browser url, for example, baseUrl/poll/vote, it doesnt work. Only if I type in the link it does that. If I get to that path by my app redirects, then it works.
Router needs to be an instance of HashRouter, not BrowserRouter.
import {BrowserRouter as Router} from 'react-router';
BrowserRouter needs special configuration and consideration on the backend to handle network requests to routes that, in fact, should be handled by the frontend.
HashRouter is easier to use because it inserts a # into the URL, effectively sending all page requests to the same base route without the server needing to do anything special.
I am new to React. I created a nested route which works perfectly on local across different devices. However, when I deploy it on GCP nginx server, the nested routing seems to have been ignored if I directly visit the path. Contrastly, it seems to work well when the path was pushed by a button, which I do not understand.
I have a routes.js
<Router history={history}>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/admin" component={Admin} />
<ProtectedRoute path="/message" component={Message} />
<Route path="/profile" component={Profile} />
<Route path="*" component={() => <h1 style={{ textAlign: "center" }}>404 NOT FOUND</h1>} />
</Switch>
</Router>
Taking in /profile, I have nested routes
const { path, url } = this.props.match;
...codes between...
<Switch>
<Route path={`${url}/seller`} component={SellerProfile} />
<ProtectedRoute path={`${url}/user`} component={UserProfile} />
<Route path={`${url}/motto`} component={mottoProfile} />
<Route path={`${url}*`} component={() => <h1 style={{ textAlign: "center" }}>404 NOT FOUND</h1>} />
</Switch>
Again, locally, all the components were rendered correctly. However, when deployed on server, visting the nested routes (ie /profile/motto) directly would return a blank page.
Despite that, when I use history.push, I am able to visit those nested routes. For instance, I have a function in another component that can successfully visit the nested routes.
const onProfile = (e) => {
const cookies = new Cookies();
props.history.push({
pathname: "/profile/user",
state: { userid: this.state.userid },
});
}
Note that profile/motto does not require a state and its not the only nested routes that behave the same.
During my search I was taken here - it's not a direct answer about nginx, but for GCP load balancer in front of a bucket. Posting since others might be looking for the same.
Here is good background information: react router doesn't work in aws s3 bucket
What worked for me is to rewrite url with /index.html similar to forwarding the 404 errors solution above. This works clean and returns 200 code, but only works for 1 level, so paths "/", "/users" work, but path = "/user/*" and trying "/user/someuser" does not work.
Here is the url map:
yourmapname.yaml:
defaultService: https://www.googleapis.com/compute/v1/projects/yourprojectname/global/backendBuckets/yourbucketname
hostRules:
- hosts:
- yourhostname
pathMatcher: path-matcher-2
kind: compute#urlMap
name: yourmapname
pathMatchers:
- defaultService: https://www.googleapis.com/compute/v1/projects/yourprojectname/global/backendBuckets/yourbucketname
name: path-matcher-2
pathRules:
- paths:
- /
- /user
- /users
service: https://www.googleapis.com/compute/v1/projects/yourprojectname/global/backendBuckets/yourbucketname
routeAction:
urlRewrite:
pathPrefixRewrite: /index.html
cloud compute url-maps import yourmapname \
--source ./gcp/yourmapname.yaml \
--global
Same principle of rewriting to index.html works in nginx.
I am new in react programming. Trying to solve issue my self but, stuck on following issue.
I have following react router code.
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
class Main extends Component {
render() {
return (
<Router>
<Switch>
<Route exact path='/' component={Content} />
<Route path='/user/:id' component={User} />
<Route path='*' component={NotFound} />
</Switch>
</Router>
);
}
export default Main
In content there are list of users with their photo. If i click on person photo it will redirect me to particular user.
I wrote my code like:
<Link to={'/user/' + userItem.id}>
<img className="useritem-img" src={userItem.photo} alt={userItem.tagline}/>
</Link>
It will open User Component properly with new URL like: http://localhost:3000/user/457365 on photo click.
But, when copy and paste same url in new tab it will not open. May be i am wrong some where.
Any help would be greatly appreciated.
Edit:
I am getting following error when i open that page:
Cannot GET /user/457365
I am not using create-react-app just simple react application.
Following is my server.js
app.use(express.static('dist'));
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, './index.html'));
});
app.listen(port, function (err) {
if (err) {
console.log(err);
} else {
open('http://localhost:' + port);
}
})
If you are getting that error that means that the server is trying to handle the routing. Therefore you should make sure that the server allows the SPA to handle the routing.
For instance, if you are using express, you probably want to do something like this:
app.use(express.static(path.join(__dirname, 'client/build')));
app.get('/api/whatever', (req,res) => {
// Whatever your api does
});
// Allow the SPA to take care of the routing
app.get('*', (req,res) =>{
res.sendFile(path.join(__dirname+'/client/build/index.html'));
});
I am trying to implement the following nested urls with react-router. My problem is that the Feed component sends both GET and POST requests to the wrong url when nested within the Router component, like this:
ReactDOM.render((
<Router history = { browserHistory }>
<Route component={ NavBar }>
<Route path='/' component={ Feed } url='/api/threads' pollInterval={ 2000 } />
<Route path='signup' component={ Signup } />
</Route>
</Router>
), document.getElementById('root'));
sends requests to http://localhost:3000/?_=1463499798727 which returns the content of index.html, which causes an error since the ajax request is expecting json data, not html, and is wrong anyway.
while
ReactDOM.render((
<Router history = { browserHistory }>
<Route component={ NavBar }>
<Route path='signup' component={ Signup } />
</Route>
</Router>
), document.getElementById('root'));
ReactDOM.render(<Feed url='/api/threads' pollInterval={ 2000 }/>, document.getElementById('feed'))
sends requests to the expected url http:localhost:3001/api/threads and returns data, and everything works normally.
As a side note, I have port 3000 set for the webpack-hot-load frontend and port 3001 set for the Express backend.
On the Express side, I have the following route set:
router.get('/api/threads', function (req, res, next) {
Thread.find(function (err, threads) {
if (err) { return next(err); }
res.json(threads)
})
})
Visiting localhost:3001/api/threads returns the expected data.
localhost:3001 returns Cannot GET '/' which is expected.
First, if a URL is intended to be used as an API endpoint and not directly in the browser then it probably does not belong in your react-router at all. Only put paths in the router that you expect to render a view in the browser. So if you want localhost:3001/api/threads to return JSON via an API call, take it out of your router.
Also, you use should organize your routes using IndexRoute. Try this:
<Router history={browserHistory}>
<Route path="/" component={CoreLayout}>
<IndexRoute component={Feed} />
<Route path="signup" component={Signup} />
<Route path="*" component={NotFoundView} />
</Route>
</Router>
Where CoreLayout simple renders it's children. I'm not sure exactly what you are trying to display for the root URL (localhost:3001) but you would use a component like above.
To use your API endpoint you can just call it in the component directly via it's full path (localhost:3001/api/threads).
I have this simple code that doesn't work. I took it from the react-router project page and modified slightly for it to look a bit better.
Setup
I have several very simple react components:
var IndexPage = React.createClass({
render(){
return (<h1>Index Page</h1>);
}
});
var AboutPage = React.createClass({
render(){
return (<h1>About page</h1>);
}
});
var NotFoundPage = React.createClass({
render(){
return (<h1>Not found page</h1>);
}
});
Also I have made a setup of react router:
var ReactRouter = require('react-router');
var Router = ReactRouter.Router;
var Route = ReactRouter.Route;
var BrowserHistory = ReactRouter.browserHistory;
var Render = ReactDOM.render;
And that's how I use react router 2.0.
Render((
<Router history={BrowserHistory}>
<Route path="/" component={IndexPage}>
<Route path="about" component={AboutPage}/>
<Route path="*" component={NotFoundPage}/>
</Route>
</Router>
), document.body)
I use BrowserHistory (not HashHistory) to avoid hash in urls.
My server is raised under IIS 10 (Windows 10) on 8080 port.
Problem
http://localhost:8080/
goes to IndexPage component. This is correct.
http://localhost:8080/about
goes to IIS 404 error page. Routing doesn't work in this case
http://localhost:8080/ttt
goes to IIS 404 error page again.
So the router doesn't see this nested paths that go after /. And it doesn't even care about whether they are correct or not.
What can cause such a strange behavior?
Thanks in advance!
Update
I've found out that the next string solves the problem with a client routing:
{this.props.children}
This is the fixed code:
var IndexPage = React.createClass({
render(){
console.log('index page render');
return (<div>
<h1>Index Page</h1>
<Link to={ '/about' }>about</Link>
{this.props.children}
</div>);
}
});
Is your server configured to map all application-paths to your index.html? You should never get a IIS 404 if your server would map all path to your index file, as it always would deliver this one.
React-router is a client-side routing solution(also works in server side, but it seems you are using .Net as the server side handler).
You can try this(Link from React-Router):
<Link to={ pathname: 'about' }>
about
</Link>
Put it in the index page's component, and click the link, the react-router will work.
Update:
{this.props.children} ref to the inner component(AboutPage, NotFoundPage).
If you change code to that:
<Route path="/" component={IndexPage} />
<Route path="/about" component={AboutPage}/>
<Route path="*" component={NotFoundPage}/>
It will works.(remove the {this.props.children} in IndexPage if there is no nesting routes).
I was able to solve my issue linking directly to URLs by adding --history-api-fallback to my webpack-dev-server command.
Setting this flag prompts webpack-dev-server to serve the index page instead of 404. The index page will then route correctly on the client side.