Single page web app (SPA) link/refresh routing design considerations and caching - javascript

What is the recommended practice of sending the SPA code to the client with routing considerations? How to send the SPA code to the client when they go directly to a link (e.g. website.com/users/user1) rather than the root path first.
An example best illustrates the question:
The path website.com/users/user1 responds with some application/JSON so that the SPA can fill in the layout with the information.
Let's say that you have a basic SPA and you are making a request on website.com/users/user1 (assume no auth is required) without having first visited the root path where it is clear that we can send the SPA code (html, css, javascript) to the client and then they can make their requests to the different routes through the web app. So the user visits websitename.com/users/user1 and the server doesn't know whether the client needs all of the SPA code first or just the JSON (maybe the most recent version is cached, or they are visiting website.com/users/user1 after having first visited website.com/ which knows to make a specific request to the server and ask for the JSON).
How is this typically handled? Is a flag, date, or something else sent to the webserver with the SPA so that the server knows the client has the SPA code? This could be done via the SPA requesting a content type of application/json on the route rather than a standard GET request? Or setting a header that the SPA sends back denoting its most recent version (this way we can use caching and if it isn't the most recent, or there is no SPA yet, a new version may be sent).
How is it recommended that the SPA handle this? Does the SPA check the URI and note that it has only just received the SPA code from the server and not the actual content from the server (e.g., user1's information). And how is it recommended that we check this? The server sends back the SPA code and sets a header denoting that the SPA needs to make another request to website.com/user/user1 to actually retrieve the actual JSON of user1's info rather than the SPA code.
EDIT: I have eventually come across this SO question and the answer more or less addresses all of my questions: How to make a SPA SEO crawlable? There are obviously many ways to handle this on both client and server side and I wanted to see the different ways people addressed the issue. (I like the way that the aforementioned question/answer deals with the issue and will likely use a similar scheme.)

I don't know what your stack is, and I can't really speak to recommended practices, but I use webpack for this. In webpack, you accomplish this by defining multiple entry points. This splits your code into different self-contained packs, so you can have different .html files that produce different code bundles.
Adapting your situation to the appropriate webpack config:
{
entry: {
main: "./index
user1: "./users/user1",
},
output: {
path: path.join(__dirname, "dist"),
filename: "[name].entry.js"
}
}
Then, in the appropriate html, you'd load user1.entry.js
I am not sure how extensible this is for a situation where each of your users has their own dedicated URL (obviously you can't name a unique entry point for hundreds of users), but at that point I'm not sure what you have is technically an SPA.
You can also consider using a routing solution similar to react-router, which allows you to grab data from the URL. eg, w/ a webpack config like above, navigating to example.com/users/user1:
users.html/users.js:
loadJSONFromServer({url-user-id})

I believe what you are asking is, how does a user visit a page that is not the home page (maybe through a link that was shared) and the app get the data it is supposed to display on that page.
They way I typically accomplish this is to initiate a data fetch in the lifecycle method componentDidMount that evaluates what data is already present, and fills in the missing pieces. You could accomplish something similar through react-router's onEnter hook (which is how I handle user auth in an SPA).

Related

How to client-side routing url that ends with file extension

Say a user gives the title of a note as 'onboarding.md'.
And the url ends up like this localhost:4000/ecme/onboarding.md.
Now upon a refresh with that url, I want my client-side router to handle it - load a component, calls an api via fetch, then loads the result into the component.
But I get a blank page with an error Cannot GET /ecme/onboarding.md.
No such error if I programmatically navigate to the note.
You can't, at least not really.
If the user is refreshing the page then the browser is asking the server for that URL.
Your client-side router hasn't even been loaded.
When you use client-side routing with "real" URLs (as opposed to hash-routing where all the route data is kept in the fragment identifier) then you must have server-support too.
Good router support would use server-side rendering so the page would be delivered complete from the server instead of being generated client side. This is efficient (no need to serve a bootstrap page and then have the client do all the work and making additional HTTP requests for data), search engine friendly, and means that if JS fails for any reason then the page will still be accessible.
There are frameworks to support this for most common SPA frameworks (e.g. React has Next.js).
The quick and dirty approach if you want to around SSR is to just configure the server to serve up the bootstrap HTML document that runs the client-side code for every URL it is asked for (or at least those not associated with a file). This doesn't have the benefits listed above and also ends up giving clients (including search engines) an HTML document even whey they should get a 404 Not Found error.

How should I handle front end routing in webpack?

I am building a website with separated front end and back end via restful api. When I develop the front end UI, I bought a template that uses webpack to generate the bundled production files.
Currently I am using pure javascript to handle the routing. For example, to show different view for logged in users compare to other users, I did the following:
if (window.location.pathname === "/") {
const token = window.localStorage.getItem("token")
// if token exists and it has not expired, show the logged in homepage.
if (token && !jwt_expired(token)) {
window.location.replace("/index-loggedIn.html")
} else {
window.location.replace("/index.html")
}
Here are my questions:
I notice window.location.replace could be pretty slow and it would take half a second to route from index.html to index-loggedIn.html. Is this the right way to do it?
I am planning to deploy the front end static files on Nginx as a web server. I guess this workflow will be like:
User sends a request to the "/" route -> Nginx responds with index.html -> The above javascript code gets ran -> Another request to "/index-loggedIn.html" gets sent to Nginx -> Nginx responds with index-loggedIn.html
Is that what actually will happen? It sounds very inefficient to me since I would like to be able to check user's log in status before serving the home page.
Is there any other frontend server/framework I can use to make the routing easier? Then I can use Nginx simply as a reverse proxy. I checked out Angular but I think it will take too much effort to convert the whole template into Angular's format. Can I use express just for front end routing?
Thanks in advance for your suggestions!
You are asking too many things with too many assumptions. Let us try to uncover them step by step.
Type of Application
Basically, you can build two types of applications: SPA (Single Page Application) or Multiple independent pages application; or the hybrid of two Multi-page SPA application. Typically, we use React/Vue/Angular to build a SPA application.
Routing in SPA application
In a SPA application, the routing is completely done on the client-side. The server plays very little role in it. We use HTML History API to achieve this (This is what you are doing by hand using replace method).
With this setup, you need a UI Server (typically build with Node.js) or a Nginx like Reverse proxy. Your server won't be aware of the routes that client-side code has defined. The server/reverse-proxy needs to do two things. First, when the request is for asset (request ending with .html, .css, .js, .png, etc.), then it should try to serve that asset. When the request is for non asset (request without any file extension https://example.com/users/), then you can safely assume that it is a request that must be handled by the client (your client-side code has defined the routing) and thus you must always and always send index.html page which then will bootstrap your SPA application.
In the initial boot sequence of the SPA, you would read the location and determine if this is a valid route. If this is not a valid route, you can redirect user (client-side only) to some default view or 404 view.
Why you would need to do this? Because initially, your user may just say https://example.com and then navigate to /users route by some action. But, if user does full page refresh after navigation, you back-end server will see the route as https://example.com/users. There you need to handle the fallback route to always serve index.html. In this case, 404 must be handled by using client-side code. All the major UI frameworks provide routing solution. You don't need to do it by hand.
Authentication
Now if you using using index-loggedIn.html for authenticated views and index.html, then from SPA perspective, you have a anti-pattern or code-smell at least. You must have a single index.html view which takes care of both authenticated and un-authenticated views. In routing world, we typically use guards to protect the views from unauthorized access.
What you are doing would currently work but as you mentioned that you see the lag of half seconds before redirect. And, that would happen on every page refresh. But that exactly is the problem with full client-side SPA apps in general. However with proper architecture, you should be able to address this.
Routing in a Multi-page SPA or multiple pages application
If you are building an application like this, then your server needs to be more involved and must handle more routing responsibility. In this case, you should probably use cookie instead of localstorage for managing your token. Browser automatically sends the cookies to the server and your server then can make the decision if it wants to serve index.html or index-loggedIn.html page.
To answer your question:
Is this the right way to do it? - It would work but it is an anti-pattern.
Is that what actually will happen? It sounds very inefficient! - It is not inefficient as long as you are properly loading the views with spinner and doesn't cause awkward UX for the users.
Can I use express just for front end routing? - It is an invalid question. Literally, you cannot use express for front-end routing. But then if you plan to implement UI server using express, then you can use it to define proper routing architecture eventually making it part of your front-end stack.
In the end, note that there are few more consideration that you need to take into account. What happens when user is doing hard refresh when he is on some route that server doesn't recognize? How to properly support bookmarking capability for your users? Also, are you building SPA or traditional server rendered application? If SPA, use client-side framework. If server rendered, then keep all the logic inside Express or equivalent code. If it is hybrid, determine the architectural boundary.
Finally, if this sounds too complex consider using hash-based routing for your SPA. That simplifies many things at the cost of not-so clean URLs.

How do I correctly deal with 404 HTTP errors when using an SPA and no server-side computation?

I am currently using Vue.js on a website project of mine.
The server that returns this Vue.js SPA will not be capable of computation or checks, and will only serve the static resources required to run the SPA in the browser, where all computation will then take place.
For SEO purposes, I want to ensure error pages are handled correctly. Currently, every URL returns a 200 OK and serves the SPA which can confuse search engines for pages that are actually supposed to be invalid. What would be the correct way of telling both users and search engines that the page is invalid if I cannot change the response the server provides?
For context, the SPA will get data from an API on another domain.
I have looked at other questions that are similar, but they normally recommend server-side checks, a dedicated 404-page redirect or a soft-404 page. Server-side checks and a dedicated 404 page will not be possible, and I have read that soft-404 pages are disliked by search engines.
Is there any proper way to go about this?
I have seen this post, but it is quite old and only suggests a no-index tag, which still makes the page valid in the eyes of search engines, just not indexable.
You can't return a 404 error without any server-side computation/rendering, because the fact that the resource/page wasn't found relies on some logic that only gets executed on the client-side in your case. Hence, your only options are the following:
If the resource wasn't found, redirect the user to a pre-defined 404 page (that returns the correct HTTP status)
Blacklist paths that are invalid inside your proxy, or whitelist those that are valid, and proxy to a 404 page on all other paths
Manually create a sitemap with your valid pages/routes
None of these options are optimal if your project grows or you have dynamic routes, but those are the limitations of client-side rendering. Hence I don't think there's a good answer to your question, since this is a well-known limitation of client-side rendering, and one of the main reasons why projects that care about SEO prefer server-side rendering.
From an SEO perspective: As long as you add all your valid pages to a sitemap, and don't link (anchor tag) to the invalid ones on any of your other pages, no search engine crawler will ever crawl or index these pages.
But if you really care about SEO and have a dynamic app with hundreds/thousands of dynamic links that cannot be added to a sitemap manually, I'd advise you to switch to a production framework like Nuxt.js or Next.js because they offer what you're looking for and much other SEO features out of the box.

Protecting "back-end" angular source files

I have a Angular system that solely talks with my Go back-end and with Gorilla I take care of my sessions for login.
I started working on my admin environment, but I wondered what would be best practice for protecting the angular code for it. It's not really a problem for security because even the admin code will just have logic, and not dangerous data, still I prefer to not have it open to just anyone in the world.
I was thinking of doing the following;
I have a mux router that catches all my resource calls (deployment with Yeoman) and I was wondering that I would make 3 exceptions there for images/admin, scripts/admin and styles/admin. These paths can then only be served if you have a valid session active. Otherwise throwing a 401 header.
Would this be a good solution or is there a more efficient way to achieve this?
If you need a valid (and preferably authorized) session to get some static assets (being them JS code, stylesheets, images...), you need to pass through the application, the stack you use is not relevant at all.
What I'd do is to point the resource to something controlled by your application, and then return either a 401 or an empty response with a X-Sendfile or X-Accel-Redirect header so the actual serving is offloaded to whatever reverse proxy you have in place.

Completely splitting the front and backend in web application

The backend(server) only provide data through api(s), all the page rendering will be done by the front through javascript(ajax).
Generally I think this is a good idea to support multiple clients, for example both the android,iOS,browser can use the same api. Also people may have more clear responsibilities, one can focused on the server or the front only.
However there are some problems I can find at the moment:
1 Reusable layout.
For pages rendered by the backend, we can use some template(layout) libraries like apache tiles to make a uniform look and feel for the while application.
While for the client rendering, none of the ejs,handlebars can fit this.
2 Authentication
Some operations maybe only open for authenticated user, normally we use:
<c:if test="${session.user}!=null">
// put the security operations
</c:if>
While for client rendering, we will have to send ajax to server to make sure the user is authenticated, and then refresh the view, which may cause the rendering steps more complicated.
3 URL parameters parsing
When the pages are rendered by the server, we can get the url parameters easily, for example the #Param #PathVariable. which means we can create any kinds of urls.
While for client rendering, we will have to build a common libraries to parse the parameters ourselves.
4 ...
I think there maybe more problems I have not mentioned yet.
Then I wonder if this kind of design desirable? If yes, are there any common practices we can follow ?
Most modern frameworks (ZK, meteor, django) go this way. Instead of putting everything into one monolith, all data services are implemented as REST or HTTP services which return JSON. The UI just knows when to make which call and is implemented in JavaScript.
Uniform Look&Feel is not something you aim for; most platforms are slightly different and users expect these differences (Apple behaves different than Android or Windows).
As for authentication, you still need to login to the services (most APIs expect a cookie or they won't work). So that doesn't change.

Categories

Resources