Applying SSR / social media sharing to selected routes in Angular 7 - node.js

We made a late decision to implement social media sharing for Angular 7 project. As a first step of the re-write I wish to dynamically update social media tags for a particular route only ('/post/').
I am able to apply SSR on '/post/' using app.use in the server.ts file:
app.get('/api/**', (req, res) => { }); // ignore api requests
app.use('/post/', (req, res) => {
res.render('index', { req });
}); // successfully serves SSR content on '/post/' but client side rendering fails to take over.
app.get('*', (req, res) => console.log('Other routes')) // 'ERR_EMPTY_RESPONSE' error
I am running off localhost:4200 for both server and client - spinning up the SSR node server with npm run serve:ssr, and spin up the client side with ng serve.
I am unsure the best way to handle requests for different routes. How can I tell Node to skip other routes and to spin up Angular on the client side? Currently it returns 'ERR_EMPTY_RESPONSE' error on other routes and only provides SSR content for '/posts/' (no client side rendering).

Related

react and node js serving from single port endpoint not found

Im making a simple app with react and node. On the react side I create a note with a unique-id and then the note can be found at mypage.com/unique-id using react-router-dom and useEffect in the loaded component to get the data from the database. This works perfect as long as my frontend and backend are being served from different ports. When I build the react app and place it in my nodejs app the app will work fine, until I reload mypage.com/unique-id and then im hit with a 404 on my server side. I have a bit of a grasp of what is going on - everything is being served from localhost:3001/ so when i reload localhost:3001/unique-id my server is looking for a route for that, which doesnt exist on my server side. I'm not sure how to connect the dots to serve my front end page at localhost:3001/unique-id with data from the server for that unique id. If i makea route on my server side it would just send back the raw data without serving the page that is suppose ot be at localhost:3001/unique-id
I had faced the same issue and it got resolved by adding * into the route
before route
app.get("/", (req, res) => {
res.header("Cache-Control", "max-age=-1");
res.header("Expires", "-1");
res.header("Pragma", "no-cache");
res.sendFile(path.join(__dirname, "build/index.html"));
});
after
app.get("/*", (req, res) => {
res.header("Cache-Control", "max-age=-1");
res.header("Expires", "-1");
res.header("Pragma", "no-cache");
res.sendFile(path.join(__dirname, "build/index.html"));
});

Node.js / Express.js + Angular router - server overwriting client view with response object when using direct link

I am building a node.js app with express, I am hosting an Angular SPA in the public folder.
The app runs and the hosting works fine when I use the angular router for navigation around the website, but when I directly try to access the link, for example: http://192.168.1.4:3000/posts, the entire body of the website is just the JSON response object, without the app
this is the Node.js code handling the get request
postRouter.route('/')
.options(cors.corsWithOptions, (req, res) => {
res.sendStatus(200);
})
.get(cors.cors, (req, res, next) => {
posts.find({})
.then((post) => {
res.status(200);
res.setHeader('Content-Type', 'application/json')
res.send(post);
}, (err) => next(err))
.catch((err) => next(err));
})
this is my angular service sending out the get request
getPosts(): Observable<Post[]> {
return this.http.get(baseURL + 'posts')
.catch(error => { return this.processHttpService.handleError(error); });
}
Post Component .ts file
ngOnInit() {
this.postService.getPosts()
.subscribe(posts => { this.posts = posts, console.log(this.posts); },
errmess => this.errMess = <any>errmess);
}
Again, when i use my Angular 5 client app hosted in the public folder, built with ng build --prod, the JSON object is retrieved from the mongodb database and is displayed correctly on my website, along with the rest of the app, the header, the body, and the footer.
it might also be worth noting that the console.log on the ngOnInit() is not displayed on the browser when using the direct link.
Any advice/fix is greatly appreciated
You have a clash of routes between angular and your express application. Angular is served up on one route (I'm guessing the / route) and then it sort of "hijacks" the users navigation. It does this by not actually changing web pages, instead it just changes the URL in the navigation bar, but never actually makes a web request to get to that resource.
You've then got endpoints on a web server listening on those endpoints. This means the moment you visit the /posts page, you're not asking angular to do anything. In fact, angular isn't even loaded because that only gets loaded on the / route. Instead you're going straight to your API.
There are ways around this, to start with many people put their API fairly separately, either on a subdomain or mounted on /api (such as /api/posts). Then your angular app can be served up on the / route. There are other techniques you can use to then allow a user to go to /posts and still get your angular app loaded.
You can use a few approaches for this such as the hash location strategy, or you can serve up your angular application from any route on the application (* in express) and load the angular app which will then take over. This second approach is most comment, it usually results in hosting your api on a sub domain and then serving your angular app on the * route of the normal domain name. For example: api.myapp.com will serve only JSON responses, but any route on myapp.com will serve the angular app, such as myapp.com/posts.

Create React App with Express API cookie authentication

My setup is thus:
Node server running Express which runs an API.
Inside a subdirectory client is a Create React App project, with the proxy field set to the URL of the Express API.
I start both processes and access the CRA in my browser, which sends requests to the Express API through the proxy.
This all works fine.
In production, I will build the CRA and serve it from the Express app, like so:
app.use('/api/:controller', (req, res, next) => {
return router(req, res, next)
})
app.get('*', (req, res) => {
res.sendFile(path.join(`${__dirname}/client/build/index.html`));
})
My only roadblock is how to handle authentication. I could quite easily do an onload fetch request back to the API when the React app is initialised in the browser to see if the user has a session, but it seems like a waste.
In development (and I guess production for that matter), what would be a good way to 1) read the cookie from the incoming request and 2) pass on the currentUser object to the index.html above (in production) or to CRA in development.
To clarify, I was wondering if there are any specific CRA recipes for handling authentication from the server, before it sends the response back to the browser? (in this dual-purpose codebase setup)
Exact issue: In development, because I'm running yarn run cra, it's spinning up it's own webpack dev server, which serves the React app. So I don't believe I can really get at the request before it gets sent back to the browser.

Separate Node and React App: Allow Express passthrough to React Router

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)
}

Using ReactJS with NodeJS

I have an application which need to interact with UI and perform various CURD operation to database in the backend,
At present I we have a angular 1.2 application which have the UI and another node application and for every and the angular application sends http request to node application for every interaction needed, so here I have two different services.
I wanted to combine these operations into a one single service means UI(React) and node in one single service, like react inside node, when I start node server I should be able to access by UI also ,I am using express in node, what is the best way to do to use ReactJS with Node in a single service.
Hope you are using express along with nodejs. You could place the react application folder inside your node application folder in some folder named client. Then from node app you could do the following to server react files:
var app = express();
app.use(express.static(path.join(__dirname, 'client')));
After all the application specific routes to serve your apis, provide the following to serve index.html for all other get requests.
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname+'/client/index.html'));
});
In order to execute http requests from a react application (it doesn't matter if you backend is node), I recommend using fetch api:
fetch(url)
.then(response => response.json())
.then(response => /* Do what you need with it */)
Axios also works quite good and provides the same result:
axios.get(url)
.then(response => response => /* Do what you need with it */)
.catch(error => console.log(error))

Resources