Separate Node and React App: Allow Express passthrough to React Router - node.js

I have two separate apps: one of them is my API in Node with Express, and the other is my front end in React with React Router.
Typically when doing smaller apps, you might have the server code colocated with your client code in the same repo, but in this case they are separate repos. I am trying to allow passthrough to React Router from Express if the route is not matched, and we traditionally do it like this:
app.get('*', function(req, res) {
res.sendFile(path.resolve(__dirname + 'dist/index.html'));
});
See the problem? I don't have the dist folder on my server, so I have no reference to the index.html available. The reason I broke out the repos is because I upload all the front end code to a CDN, and can just proxy the network requests to my API.
How do I allow the passthrough from Express to React Router if I don't have a file or path to resolve in the res.sendFile catch-all ?

A bit of a hack, but I realized that this would probably work and I tried it out and it does indeed. You can probably take this a step further and even extract the referrer from req.headers.
request("https://yourwebsite.com/index.html",(error, response, body) => {
res.send(body)
}

Related

Connect express backend to React frontend (in the same server if possible)

I saw a lot of ways to connect React frontend to express backend (REST API) and i don't understand which one of the them is the most common, organized and friendly. (Axios, componentDidMount function and so on..).
My project divide to backend and frontend libraries which includes a connection to mongoDB in the backend.
I am new to React so i will appreciate any recommendation.
You can easily have both on the same server, all you need to do is. Make an express route that servers your react app's index.html.
app.get('/', (req, res) => {
res.sendFile('./public/index.html');
});
Also, don't forget to serve your static files (css, fonts, etc) using express's own middleware.
app.use(express.static('public'));
After you have done that, you can have your API at /api.

Angular Universal app can't make post requests to my Heroku node API

My website is a SPA built with Angular, but it uses SSR with Angular Universal to provide crawlable and social media sharing content.
All GET requests in my server are handled by Universal like this:
app.engine(
'html',
ngExpressEngine({
bootstrap: ServerAppModuleNgFactory,
providers: [provider]
})
)
app.set('view engine', 'html')
app.set('views', __dirname)
app.use('/', express.static('./dist', {index: false}))
app.use('/', expressStaticGzip('./dist', {
enableBrotli: true
}))
app.get('/*', (req, res) => {
res.render('./dist/index', {
req: req,
res: res
})
})
and my pages contents are provided by Angular Services POST requests built with the same queryParams of the requested url.
One example:
If the user visits the url https://mywebsite.com/products?page=1&itemsPerPage=12 (GET request by default), the Angular Universal app and the Angular Router dynamically build my page template and the products list is provided by a Service that triggers a POST request to this URL: https://mywebsite.com/request-products with the following params in body:
{
page: 1,
itemsPerPage: 12
}
Then the Universal App builds the template with some *ngFor directives to populate it before serving it to the client.
This approach makes all my pages visible to webcrawlers and I also get the benefits of a Single Page Application.
When I'm testing my app, I build my Angular app, both Browser and Server builds, and set my environment like this:
export const environment = {
production: true,
apiUrl: 'http://localhost:7070/'
}
and serves my app in localhost, it works perfectly, without errors. My POST requests, like mentioned before, are all handled perfectly. But when I try to set my apiUrl to 'https://mywebsite.com/' and serve my app also in localhost, to access directly my API hosted in Heroku, I just can't access my POST routes.
My node express server app in Heroku is configured to accept requests from other domains, I can access it normally in my localhost server, but when I try to access it through my Angular Universal server build, it just won't work.
I know that I have to use absolute URLS in my Universal Apps, and I'm doing it already, but it's not working.
Does anyone know what I have to do to access external APIs in my Angular Universal Apps via https?
Thanks!
I've found the problem, and it's something really simple.
It turns out that I must use 'www' in my absolute url, like this:
'https://www.mywebsite.com/'
Now everything works perfectly, both from my localhost and my heroku servers.
Thanks to everyone that took some time to read my question!

Angularjs4 with express generator

Just developed a simple angular 4 application with this tutorial
https://scotch.io/tutorials/mean-app-with-angular-2-and-the-angular-cli
But how can i integrate angular 4 app to a express application that generated with express generator??.
One approach is REST API's method that is express JS app use as API provider and the angular application communicate with REST api.
But i would like to serve the angular application from express application itself..
I think this will helpful to you. First I assume, you use express server to handle some api request and let's say those routes are begin with /api. they can be differ from yours.
First build your angular application by ng build and it will create a folder called /dist in your project folder.
Copy that folder in to your express project /public folder. You have to put them in an static routed folder. /public folder is a default static route folder. That is why I put it there. If you have your own one, you can put there too.
edit your app.js file as follows
// Set our api routes
app.use('/api', api); // API router definitions.
// Catch all other routes and return the index file
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, 'dist/index.html'));
});
Hope you will help this way. Thanks.
You can serve it through your server like this:
// Catch root route and return index.html
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'app/index.html'));
});
// Catch all other routes
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'app', req.originalUrl));
});
But be careful to set the right path to your index.html.
There is a technology stack called MEAN which means (coincidence :) MongoDB, Express, Angular and Nodejs. So this is basically what you are looking for.
Altough you can create you own file structure, there is a mean-cli similar to Express-generator and the angular-cli. You can find it here.
In order for Angular to work properly (like requesting a site like example.com/something and then also activate the route something) I always return my index.html for any request and setup express to return all static files as well. Then I create a route /api which handles all my REST api requests.
Make sure to first setup your /api route, then your other static files from angular like bundle.js and finally index.html as route **.

create-react-app with Express

I need to query a database and I'm using create-react-app. The library to connect to the DB (pg-promise) does not work with Webpack and needs to be running on a Node server.
So I installed Express and have this:
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, '..', 'build', 'index.html'));
})
How can I load data from the database from the React pages? I though of using request but how can I make a request to my own server? And what should I add to the lines of code above? I think it would be something like:
app.get('/query/:querybody', (req, res) => {
// process and return query
})
Is this right? How can I make it work with a SPA?
Probably the most friction-free method would be to have a separate app.js or server.js along side your CRA application. You can use a tool like concurrently to run both your React app and the express app.
The trick is to serve your express app on a different port than the default :8080 that CRA serves on. Usually 8081 is a good choice, as it's a common convention to use port numbers that are close together when developing.
In your React app, you will need to make sure you use the full URL for the express endpoint: http://localhost:8081/query/...
On the server side you are going in the correct direction: you need to setup endpoint which will respond with data based on request. In you example you setup an endpoint for a GET HTTP request. If you will need to pass a complex request (for example add new record to database), consider using POST HTTP requests.
On the client side (in the browser) you will need a library that will assist you in sending requests to your server. I can recommend to try Axios (https://github.com/mzabriskie/axios). Usually if you omit protocol, server name and port, request will be sent to the server from which the page was loaded:
http:127.0.0.1:8001/api/endpoint => /api/endpoint

How to use AngularJS routes with Express (Node.js) when a new page is requested?

I'm using Express, which loads AngularJS from a static directory. Normally, I will request http://localhost/, in which Express serves me my index.html and all of the correct Angular files, etc. In my Angular app, I have these routes setup, which replace the content in an ng-view:
$routeProvider.when('/', {
templateUrl: '/partials/main.html',
controller: MainCtrl,
});
$routeProvider.when('/project/:projectId', {
templateUrl: '/partials/project.html',
controller: ProjectCtrl,
});
$locationProvider.html5Mode(true);
On my main page, I have a link to <a href="/project/{{project.id}}">, which will successfully load the template and direct me to http://localhost/project/3 or whatever ID I have specified. The problem is when I try to direct my browser to http://localhost/project/3 or refresh the page, the request is going to the Express/Node server, which returns Cannot GET /project/3.
How do I setup my Express routes to accommodate for this? I'm guessing it will require the use of $location in Angular (although I'd prefer to avoid the ugly ?searches and #hashes they use), but I'm clueless about how to go about setting up the Express routes to handle this.
Thanks.
with express 4, you probably want to catch all requests and redirect to angularjs index.html page.
app.use(app.router); doesn't exist anymore and res.sendfile is deprecated, use res.sendFilewith an uppercase F.
app.post('/projects/', projectController.createProject);
app.get('/projects/:id', projectController.getProject);
app.get('*', function (req, res) {
res.sendFile('/public/index.html');
});
put all your API routes before the route for every path app.get('*', function (req, res){...})
I would create a catch-all handler that runs after your regular routes that sends the necessary data.
app = express();
// your normal configuration like `app.use(express.bodyParser());` here
// ...
app.use(app.router);
app.use(function(req, res) {
// Use res.sendfile, as it streams instead of reading the file into memory.
res.sendfile(__dirname + '/public/index.html');
});
app.router is the middleware that runs all of your Express routes (like app.get and app.post); normally, Express puts this at the very end of the middleware chain automatically, but you can also add it to the chain explicitly, like we did here.
Then, if the URL isn't handled by app.router, the last middleware will send the Angular HTML view down to the client. This will happen for any URL that isn't handled by the other middleware, so your Angular app will have to handle invalid routes correctly.
I guess I should have clarified that I wasn't interested in using a template engine, but having Angular pull all of the HTML partials on it's own, Node is functioning completely as a static server here (but it won't be for the JSON API. Brian Ford shows how to do it using Jade here: http://briantford.com/blog/angular-express.html
My app is a single-page app, so I created an Express route for each possible URL pattern, and each of them does the same thing.
fs.readFile(__dirname + '/public/index.html', 'utf8', function(err, content) {
res.send(content);
});
I was assuming I would have to pass some request variables to Angular, but it looks like Angular takes care of it automatically.

Resources