How to route a Node.js Single Page App? - javascript

I'm trying to design a Single Page App using Node.js that has multiple tabs, and on each tab can contain a number of images or scripts, so I'd like to prevent loading the content of every tab each time a user visits the page. So ideally when a user switches tabs the server would send the html which contains the images and scripts and the client would display that without reloading the page.
The reason I chose the SPA design is that outside the tabs would be a chat system amongst other things running in the background that I don't want to resend to clients each time a redirect happens.
So my question is what is the best way to send the html, or is there a better way in general that still uses the SPA design?
Would the best way be to send the html in the router to the client, then use ajax to prevent a page refresh and add new tab html and remove old tab html after that?

You can use Express.js with only one route.
This route could be called:
var app = require('express')();
app.get('/',function(req, res) {
res.sendFile(path.join(__dirname + '/index.html'));
}
Note that:
This is a GET route
It just sends the file contents with sendFile
After that you can have many API routes to fetch the data for your tabs. I would recommend these routes to be like this:
app.post('/about',function(req, res) {
res.json({ data: "your data here" });
}
app.post('/contact',function(req, res) {
res.json({ data: "your data here" });
}
On these routes note that:
These are POST routes
Only a JSON is returned

SPA means "single page app", and you will only have a single HTML shell that a JavaScript framework will then handle the "pages" and HTML after that

Related

AngularJS manual routing with ExpressJS

I am using AngularJS in the client side combined with ExpressJS in the server side.
When pages are loaded in the browser all works fine there: clicks on the various button and differents angular routers are okay.
The problems come when a generated url need to be put in the address bar manually.
Here are the details of this scenario:
the server generates a URL (e.g. "http://localhost:9000/details?card=5" and sends it via email
I receive the email, open the url from it
the browser opens on "/" instead of staying on "/details" internal/client route
On the details.controller.js of details.html (client side) I can see that the request for card number 5 is sent and arrives to the server (so I am almost sure that the details.html page is loaded) but angular routing must be very quick relocating the user view on "/".
I am not sure on the exact flow, but I am assuming that:
click on the link in the email
the OS or email client redirect "http://localhost:9000/details?card=5" to the browser
the browser receives "http://localhost:9000/details?card=5" and performs a GET to the server
the server receives the GET above
maybe the server is calling "/index.html" since express static is configured as:
app.use(express.static(path.join(__dirname, config.client.path), {
index: 'index.html',
}))
Maybe I am little confusing something, the project is not mine and I have not so many skills in (the very old) AngularJS.
Could someone explain to me how the server could "say" to the client to move on the angular "/detail" route (including the query param card)?

How to run middleware on each React page in express server

I'm running an express server and if no other certain URLS aren't met, the user is redirected to the React App:
app.get("*", verify, (req, res) => {
if (maintenance) {
return res.sendFile("./maintenance.html", { root: __dirname });
}
return res.sendFile(
path.resolve(__dirname, "../frontend/build", "index.html")
);
});
This successfully runs the verify middleware needed to check if the user making the request is logged in, if not it redirects them to another service for them to login.
I would've thought that since all URLS that are being used are going through the whole web app is going through this express server, the verify middleware would've been executed no matter what page the user is on. However, this middleware is only executed upon either opening the site on a browser that hasn't been to the site yet (not cached) or if the user force refreshes the page.
I'm new to React and this has led me to believe that React loads the whole app, and upon each change of page (for example from /home to /profile) it doesn't change the url, just loads the portion of the app that's already loaded and so no matter what page the user is on, express will always see that the user is only on the root domain.
How would I get pass this without doing any of this logic on the frontend side. E.g. run the verify function before each page on the react app is loaded into view
Since React is all client side rendering you will need to handle that middleware in your React router, not your Node router. On the frontend we call this "middleware" protected routes.
The overall point is, you will need to protect those routes on the frontend, as when you change a page in React, Node does not know about it (because React is client side rendered).

How does Express and React Routes work on initial GET request from browser?

I'm new to the react world and to the fullstack world as a whole but I've searched endlessly for an answer to the following and some guidance would be really appreciated.
I'm creating an app using React and Express. It requires authentication so I was planning on using Passport to help. The client side JS uses React Routers to navigate through the website. That's all fine but my issue is with the initial GET request made by the browser.
I'll first describe my specific app requirements and then generalize what I don't understand.
As I said, my application requires OAuth2 authentication. If you try to GET a path on my website and you're not logged in, it should just load the login page. If you are logged in, then load as normal and find your path. Similar to facebook, I'd like the login URL to be the same as the "feed" page. So similar to how facebook.com '/' route is either the login page or your new feed depending on whether you are signed in, I want the same thing.
From what I understand, Passport authenticates on the back end by checking the request header. So I understand that I should have some kind of middleware that says "if user is signed in, continue down the routes otherwise render sign in page" ... How is this done? What would the code look like? My only experience with Express was from an intro class which used res.render to send back an HTML file and pass it through some template engine like handlebars. But I have no idea how it'd work with react routes. Would i still use res.render()? Something else?
Let's say my index.html has the root div to inject the react into. If I had to guess, I'd send back that index.html page with the .js file with the routes and somehow on the backend send back the route I want it to match on my react routes (either the login one or the user requested)??
More generally, I guess I'm just confused how the initial request to a website using react routes is done. 1) How does the server interact with everything to render what I asked for? 2) What would the code look like for that. My only experience with React is from a basic Udemy course that just used "react-scripts start" to render the page.
After spending the entire day Googling this question it led me to SSR which is a rabbit-hole of its own and I'm not even sure if its what I need to help me. Is it?
I'm clearly missing some fundamental knowledge as this is really tripping me up so if you have any resources to learn more just post them. Thanks!
I understand your struggle as I've had to go through it myself when combining front-end with back-end, specifically React and Node. So first things first, we know that the browser/client will always initiate a request to the server, so how does React Router take control of the routes? Well its plain simple actually, all you have to do is return the entire react app from any route from your express server. The code will look something like this:
const express = require('express');
const app = express();
app.get('/*', (req, res, next) => {
// Return React App index.html
});
app.listen(3000);
Once the react app renders on the user browser (don't worry about paths, as react will automatically render according to the URL based on the code you wrote in the client side, it will also take care of authentication vs feed page when it will scan for your local storage, cookies, etc), it will take control of routing, instead of a request going to the express server. But what happens when we request data from our server, well it returns react app on each route so we need to setup an api route to handle any data requests.
app.get('/api/v1/*', (req, res, next) {
// Return some data in json format
});
Hopefully, this gives you insight about what you were looking for.
I think the fundamental gap you're struggling with stems from that lot of those 'intro courses' shove the entire browser client into the application server to get things up and running quickly, as in, the Node server renders the entire React app AND operates as an API...
// Ajax request from React app to: http://example.com/api
app.use('/api/*'),()=> {
res.send({ <!-- some JSON object -->})
})
// User visits in browser: http://example.com/**/*
app.use('/*',()=>{
res.render(<!-- entire React App sent to browser -->)
})
The first request (assuming the user doesn't visit /api/* ) will just send down the React bundle. Further user navigation within the client would generally send XHR requests (or open WebSockets) from the React app to Express routes running on the same node program.
In many situations it makes sense to have these parts of your program separated, as by having react delivered from a completely different location than where it requests data. There's many reasons for this, but optimizing computing resources to their differing demands of CPU, memory, network .etc and manageability of code/deployment are the big reasons for me.
For example...
User visits: http://example.com *
Nginx, Apache, a 'cloud proxy' .etc direct the traffic to a static React bundle, which has no authentication and never makes contact with your Node server.
If the user has Authenticate previously they will have token in local storage (if you're using JWTs for Authentication) and your React app will be configured to always check for these tokens when you first it is initially loaded.
If the user has a token it will send an Ajax request in the background with the token as a Header Bearer and will send back user data, then redirect them to an 'Authenticated page' like the FB feed you mention.
If they don't have a token or the token Authentication fails then React will redirect them to the Login or Registration page
React
React basically high jacks the browser's native 'location' functionality (whats displayed after you domain name). So any events after the initial page load (buttons clicks and such) are handled entirely by React internally and uses those routes to determine what to display or what data to fetch from the API through Ajax (XHR).
If the user performs a hard page reload then that request will go back to the server and it will perform the whole cycle over again
React Router
Allows you to do 2 things simultaneously...
Manipulate the browser Location and History objects.
Use that History and Location information elsewhere by detecting changes and sending off events.
SSR
I've only toyed around with SSR so I can speak to it, but its provides extremely low latency for initial renders, doing it in 1 network request, so you want to use it areas of your program where thats important.
Not sure if this answers you question, but let me know if you would like me to elaborate on anything or provide some more detailed resources.
SSR is a little bit confuses for developer that has less experience, let forget it for now.
It will be more easier for you to assume that frontend JavaScript (React) and backend Javascript (NodeJS) are two separate apps, and they communicate to each other via API.
here the code that show Login component and Feed component depending on whether you are signed in
import React, { Component } from "react";
import axios from "axios";
class Home extends Component {
constructor() {
const accessToken = localStorage.getItem("accessToken");
this.state = {
accessToken,
feeds: []
};
}
componentDidMount() {
if (this.state.accessToken) {
axios(`api/feeds?accessToken=${this.state.accessToken}`).then(({ data }) => {
this.setState({
feeds: data
});
});
}
}
render() {
if (this.state.accessToken) {
return <FeedsComponent feeds={this.state.feeds} />;
}
return <LoginComponent />;
}
}
and this is your backend
const express = require("express");
const app = express();
app.get('/api/feeds', (req, res, ) => {
const feeds = [
{},
{}
]
res.status(200).json(feeds);
});
app.listen(3001);
just keep in mind that they are two separate apps, the can be in two different folder, different server, different port.
Simply point Express to the folder containing your React files or build files.
app.use(express.static(__dirname + '/dist'));
where 'dist' contains the build files
See docs for more details

Node.js and Express - Redirect after Login and Download

I have an express app that allows a user to login and download data files. There is also a home page after the login. If a user enters the URL to a specific file without logging in, the app will first ask the user to login, which is by design. However, after the user logs in, the file is downloaded without redirecting the user to the home page. I was wondering if there's a way to allow the user to login, redirect to the home page, and then download the file with one click after the user logs in. It's kind of confusing now because the user is stuck on the login page after successfully logging in and downloading the file. Below is a snippet of the code. I am using express 4.x:
app.get('/dat/:file(*)', routes.ensure_authenticated, function(req, res, next) {
var path = __dirname + '/public/dat/' + req.params.file;
res.download(path);
});
app.use('/', serveStatic(__dirname + '/public'));
// dat directory requests
app.use('/dat', routes.ensure_authenticated, serveIndex( 'public/dat', { icons: true }));
I can't think a way of doing it server-side only.
You could trigger client-side the redirection to the download.
Something like redirecting to /#download=link_or_id and parsing the hash with JavaScript to get the final download link (the link_or_id thing). Then, after the download is triggered, remove the hash from location so the download isn't triggered again when the user reloads the page.
Also, instead of using JavaScript, you could do something similar redirecting to /?download=link_or_id and server-side inserting a meta refresh tag inside <head>.
You could programmatically send an ajax GET request from the client side via a setTimeout or on document load, so it would be something like:
$(function() {
$.get('/dat/filename', function(){
//enter code here
});
}):

Express js redirect - doesn't change url and doesn't load static files

I'm using express js for the REST interface of my application, on node js.
I'm also using jQuery Mobile for my client side pages.
I'm using redirect to change pages if the user tried to enter a location they're not allowed or can't access.
For some reason the url doesn't change, and as a result it doesn't load the css and js files.
I read in another place hat this is happening because of jQuery Mobile and they suggested to use rel=external.
I don't know how to use it together with the express interface.
Any suggestions to comments on my problem?
I'm guessing you're sending a POST request and trying to redirect server-side, like so:
app.post('/location', function(req, res) {
if (!allowed) {
res.redirect('/otherpage');
} else {
// do stuff
}
});
You can't redirect to another page on a POST request. But you can send a redirect location with res.send({ redirect: '/otherpage' }), and then have the frontend do the work, something like:
if (res.redirect) {
document.location.href = res.redirect;
}

Categories

Resources