How to use Express routes with React.js frontend? - reactjs

Can I use Express routing for sending the content of the pages? In classic HTML, you can do:
app.get("/admin", (req, res) => {
res.sendFile("admin.html"); // or res.render() for templating engines like EJS
});
Unfortunately, I am not able to use Express routing for serving the React pages. The only way I found was to use React Router, which technically gets the job done, but then I can't do anything else on the server-side, like validating the login. I could technically make an API endpoint on the server-side for that and then request it within the React JSX page itself, however, that'd be inconvenient for many reasons:
Having the requests be sent only after the page, which means tons of loading indicating and possible issues like the user being able to see the content he should not be able to see at all.
Most of the "on-page-visit" API requests would need to be protected with keys, like the Authorization key
If the API request is done anywhere on the client-side, the keys are insecure and also the response can be manually altered by the user which creates huge security violations.
If there was a React renderer for Express, in which you could render a React JSX file directly from the page (e.g. res.render("page.jsx")) which would compile React and send the pure HTML, that'd be great! But of course, I haven't been able to find such and I doubt there are any.

Yes you can, but only only with the bundled appilcation. You simply send the index.html you find in the build folder of your react application.
Would look like this:
Assuming that is you express index.js & that the react application lives in a "client" directory of your applicaiton.
const path = require("path");
app.get("*", (req, res) => {
res.sendFile(path.resolve(__dirname, "client", "build", "index.html"));
});

Related

How to route for a react-router app using express?

So I have a backend running in expressjs and I have multiple routes on it. Now I just followed this tutorial to set up a RESTful api on express. Now I want to switch to full react on the frontend, so that I will have an api running in the backend to get things from the database and am thinking using fetch from react to get that data. I saw many people say that is the best way to do it. But now there is an issue, I am not sure how to route for this. I have react-router setup so I am assuming I would use that. But how can I serve these files to the client side? How can I make sure every route except /api routes just serve my js files? Like I have a built folder already with an index.html and main<hash>.js. I am running them easily but how can I intergrate them with express? I was not able to find any answers to this. How can I route for a reactjs app to be served using expressjs? and also I saw a tutorial telling me to use a * route but that means even my api routes will only point to that.
There are three ways basically to render an application.
One is Server Side Rendering, the other one is Client Side rendering and the third one is Isomorphic rendering.
So if you are defining your routes in Nodejs and navigating the application through those routes than it will be entirely server side rendering.
I saw a tutorial telling me to use a * route but that means even my
api routes will only point to that. ? How can I make sure every route
except /api routes just serve my js files?
Regarding this what you can do is
server.get('/api', (req, res, next) => {
//You can handle the request here
})
server.get('*', (req, res, next) => {
//You can handle the request here
})
You can define your route in this order.So by this way any call to the '/api' will be handled by the first route and all the other request will be handled by the second route.
Now I want to switch to full react on the frontend, so that I will
have an api running in the backend to get things from the database and
am thinking using fetch from react to get that data
Here you dont need this.It will be an client side rendering completely
server.get('*', (req, res, next) => {
//You can handle the request here
})
For this you can create an react app from scratch or use some boilerplate (https://github.com/facebookincubator/create-react-app).
There you can define all the routing and simply call the url http://localhost/api/xxxx and get the data and you can use this data in the frontend.In this case there will be a Nodejs Server which will be serving the frontend and the expressjs server will he hosting the 'api' service to get data from.
I have react-router setup so I am assuming I would use that. But how
can I serve these files to the client side?`
How can I route for a reactjs app to be served using expressjs?
The Reactjs app when compiled is a combination of static files comprising mainly of html, css, javascript. If you want your app to be served by your express.js server then you need to use isomorphic rendering. It is by far the best approach for rendering application as it is good for SEO and initial fast page load. It comes at the cost of a complicated setup. In this case, whenever the page refreshes or the first request comes, express will serve the first page (index.html) and index.html will contain the required static(bundled) js and css files for client side rendering. The first rendering will be done by the express server and the subsequent rendering will be done by browser itself.

Handle redirects with OAuth 2.0?

What is a good way to handle redirects in React Native with OAuth? There are external APIs I need to call, so I’ve registered my app, but I’m unclear what the redirect URI should be. For a web app, it would make sense how to handle this, but I’m not sure with React Native.
What you need to do in React Native is setup your application for deep linking. A deep link is a way for another application or in this case your browser/WebView to say "Hey! I'd like to pass this information back to a native app".
Setup:
Setup a url scheme in Xcode. This will allow you to redirect to url's formatted something like this myApp://oauthLogin
Setup Linking
From there you should be able to create an event listener for the Redirect URI that you pass to the oauth service, in this case your deep link.
componentDidMount() {
Linking.addEventListener('url', (url) => {
console.log(url);
// => myApp://oauthLogin?authCode=abc123
});
}
You will have to add extra code the make sure the url is in the correct format but i hope that gets you closer!

AngularJS and NodeJS http-server: rewrite URL

I'm using NodeJS http-server to code an AngularJS App.
I'm having a problem. When I try to access directly the url in the browser the Angular does not intercept the URL to show me the content.
If I type the URL manually like: http://127.0.0.1:8080/#!/clients it works, but not when I type directly: http://127.0.0.1:8080/clients
I want the http://127.0.0.1:8080/#! as default in the http-server.
I'm using in AngularJS the html5 mode and the hash prefix:
$locationProvider.html5Mode(true);
$locationProvider.hashPrefix('!');
Is there a way to rewrite the url from http-server with the default /#!/ before the address?
Note: Below is an example of a more complex Express.js URL rewriting situation, where you may not wish to "catch all" routes, but instead discern between routes for views and routes for the server-side api. All solutions I could find only showed a generic catch-all approach, which did not fit practical application for an app whom requires access to server-side routes. If you do simply want the "catch all", see the other yellow note at the bottom of this answer for links on how to set that up.
If you turn off html5 mode, by default, you should be in hashbang (#!) mode...Turning on html5Mode allows you to remove the hashbang (#!), if you wish.
Here's more about the different modes:
https://docs.angularjs.org/guide/$location
HTML5 mode being enabled, gives you normal looking, non-hashbang URLs. Normally, when HTML5 mode is enabled, you'll hit the issue you described where pages within your app will load OK internally, but if you try and enter the URL directly into your browser (or access it via a bookmark), you'll get a Cannot GET error.
First, be sure you've set a <base> tag in the <head> your primary index file, like this:
<head>
<!-- all your script tags, etc etc -->
<base href="/">
<!-- rest of your front-end dependencies etc -->
</head>
That way Angular will know which is your primary index to load partials within.
Secondly, I will try and tell you how I approached re-writing my URLs in Express to solve the issue you have described. My answer may be somewhat incomplete, as I am still learning, and in truth, I do not fully understand why, once HTML5 mode is enabled in Angular, that the routing does not work properly. There also may be better ways to approach the problem as opposed to how I solved mine.
It seemed that once I switched to HTML5 mode, Angular intercepted my routes and was causing an issue when I was trying to use the $http service to request server-side api routes.
It seemed like turning on HTML5 mode basically took over all of my routing, and I had to find a way to tell Express to either pass a route to Angular or to continue the route (away from angular) using next(). This was my best assessment.
What I Did:
Enabled HTML5 mode [as you have done in your example], and set a
<base> in my index file (as noted above).
Rewrote my routing in ExpressJS using the native express.Router():
See: https://expressjs.com/en/guide/routing.html
At the very bottom of that page are instructions for express.Router()
I'll show you how I did it below
My Approach/Pseudo-code:
I setup a method in my Router so that if the incoming request contained /api/ (checked via regex), I would invoke ExpressJS's next() method and continue along in the route, which would hit the server controller. Otherwise, if the URL did not contain /api/, I the appropriate view page was delivered, in which Angular took over.
How I setup my express.Router():
I created a middleware folder in my app and created a file called api-check.js.
// Setup any Dependencies:
var express = require('express'),
router = express.Router(), // very important!
path = require('path');
// Setup Router Middleware:
/*
Notes: The `apiChecker` function below will run any time a request is made.
The API Checker will do a regex comparison to see if the request URL contained
the `/api/` pattern, to which instead of serving the HTML file for HTML5 mode,
it will instead `next()` along so the API route can reach the appropriate
server-side controller.
*/
router.use(function apiChecker (req, res, next) {
console.log('/// ROUTER RUNNING ///'); // will print on every route
console.log('URL:', req.originalUrl); // will show requested URL
var regex = /(\/api\/)/g; // pattern which checks for `/api/` in the URL
if (regex.test(req.originalUrl)) { // if the URL contains the pattern, then `next()`
console.log('THIS IS AN API REQUEST'); // api detected
next();
} else { // if the URL does not contain `/api`:
res.sendFile(path.join(__dirname, './../../client/index.html')); // delivers index.html which angular-route will then load appropriate partial
}
})
module.exports = router; // exports router (which now has my apiChecked method attached to it)
How I added my express.Router() to my Express App:
Depending upon how your code is modularized, etc, just go ahead and in the right place require your module (you will have to adjust the direct path depending upon your project), and then app.use() the module to intercept all of your routes and direct them to your Router:
// Get Router:
var apiCheck = require('./../middleware/api-check');
// Use Router to intercept all routes:
app.use('/*', apiCheck);
Now, any route (thus the /*) will go through the express.Router() apiChecker() function, and be assessed. If the requesting URL contains /api, then next() will be invoked and the server-side controller will be reached. Otherwise, if the /api slug is not detected in the URL, then the primary index.html base file will be sent, so that Angular can deliver the appropriate view via $routeProvider.
Note: If you don't need to discern between incoming routes, and just want to "catch all" incoming routes and hand back your <base> index file, you can do as outlined in another stackoverflow answer here. That answer uses app.get() to catch all GET requests to hand back your index. If you also need to catch POST requests or others, you may want to instead use app.all(), in place of app.get() in the aforementioned example. This will catch all routes, whether GET, POST, etc. Read more in the Express documentation here.
This was my personal solution, and there may be better, but this solved my problem! Would be interested to know what others recommend! Of course the downside to this, is that I have to build all of my internal api routes to include /api/ in them, however that seems to be OK in design overall, and maybe even useful in keeping me from confusing my routes from front-side views.
Hope this at least helps somewhat, let me know if you need any clarifications :)

Lazy load components requiring authentication with react-redux

I'm trying to create a website using react+redux.
I've already setup jwt authentication on the server.
What I'd like to do is to lazy load some of my components/containers and reducers so that they can be downloaded only by authenticated users. I already know how to hide components from unauthenticated users (client side), but I would prefer to prevent them from downloading the relative javascript code.
I am using webpack, and I've already looked into react-router and require-ensure (https://stackoverflow.com/a/33044701/2920112), but this approach doesn't seem to handle authentication at all.
I've also considered using fetch in some way (probably bundling the private code separately with webpack), but I wouldn't know what to do with the bundle once I fetch it.
Am I approaching the problem in the wrong way?
The only alternative I see is to provide two HTML files, one loading a webpack bundle with only the public content, and one downloading also the private code. This however seems really suboptimal.
What is the correct approach?
We solved this by using react-router:
<Route
key="secured_component"
path="/secured"
onEnter={handleEnterSecuredComponent}
getComponent=({nextState, cb) => {
require.ensure([], () => {
cb(null, require('YourComponent').default);
});
}}
/>
...
const handleEnterSecuredComponent = (nextState, replace) => {
const {login: {success}} = store.getState();
if (!success) {
replace('/login');
}
};
So your login page should set in redux {login: {success: true}} if user is authenticated.
If an authenticated user tries to access /secured, he will be redirected to /login.
the require.ensure does not play any role of authentication. It just an entry point for webpack to split the chunk of js files for lazy loading.
For anyone still looking into this, React has now added the ability to split the codeand do lazy-loading when using webpack:
https://reactjs.org/docs/code-splitting.html
While this by itself is not enough to require authentication for the lazy-loaded modules, it can be paired with some authorization mechanism living on a reverse-proxy.

Isomorphic Javascript Routes with React-Route vs REST API Routes

I have been studying react/flux/react-router and how pre-rendering virtual DOM in server happens. Calling Router.run() and renderToString in the server will take care of pre-rendering the page in the server and lazily loading and downloading the rest of .js files to the client. React-router deals with UI URLs in any scenario (either client or server). This is not necessarily the same as REST API URLs of the server.
What is the best practice to add routes functionality If I want to use the backend for a native app with REST features as well. should I have a complete set of routes definitions for express.js and re-define all the routes in react-routes as well?
React routes are not necessarily similar to express routes (can have more or less route patterns). So replicating route definitions seem inevitable. Is that correct? even this example seems to be doing the same thing.
I was hoping to find a way to reuse routes definition or something more DRY.
You don't want to duplicate routes on a client and server. See flux examples from Yahoo: https://github.com/yahoo/flux-examples/tree/master/react-router
Then, just specify API request before the react router on the server. E.g.:
var express = require('express');
var server = express();
// Static files
server.use('/assets', express.static('src/assets'));
server.use('/build', express.static('build'));
// Declare API handling:
require('apiRouting')(server);
// Decalre react-router handling
require('./routing.jsx')(server);
// In the apiRouting.js:
module.exports = function (server) {
server.get('/api/methodA', function (req, res) {
// body...
});
server.get('/api/methodB', function (req, res) {
// body...
});
};
Also, there's a library that makes it so you can build your APIs in an isomorphic fashion, and re-use it in the client and server without bloating or breaking the bundle. This is what we're currently using in a big single-page application.
It's called Isomorphine, and you can find it here: https://github.com/d-oliveros/isomorphine.
Disclaimer: I'm the author of this library.

Resources