How should I handle front end routing in webpack? - javascript

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.

Related

Difference between Next 12 client-side routing and Next 13 server-centric routing

In the documentation for Next 13, it says like this:
Unlike the pages directory which uses client-side routing, the new router in the app directory uses server-centric routing to align with Server Components and data fetching on the server. With server-centric routing, the client does not have to download a route map and the same request for Server Components can be used to look up routes. This optimization is useful for all applications but has a larger impact on applications with many routes.
Although routing is server-centric, the router uses client-side navigation with the Link Component - resembling the behavior of a Single-Page Application.
Please can someone explain the difference between client-side and server-centric routing in a bit more detail? It seems to me that in both cases components are rendered on the server, the client gets HTML and gets hydrated with JS. I'm not sure what the difference is except how the data is fetched.
In server-rendered components, the entire component gets built on the server and then sent to the client. the server does not send any javascript bundle, any dependency, and client will not parse anything. server will complete and sent it to the browser and the browser will just display it to the user.
That is why when you decide which components should be server-side rendered or client-side rendered components, you need to look at the component to see if the component has some sort of user interactivity, for example, button click. if there is a clickable component, that component should be rendered by the browser because the browser has to know the logic of how to handle the click once the click event occurs. The server will send the client related js code.
Since your component is rendered on the server it will be easier to fetch the data from the database. Because your server will not send any heavy library to the client, (browser has to parse javascript, and parsing always take s time) this increases the performance and initial loads will be significantly faster.
In app directory you can still use client components with use client directive on top of the page, but overall goal should be using as small amount of client components. Our goal is to minimize user wait time.

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.

why is SSR faster than SPA and vica versa?

I've been reading a lot about SPA vs SSR and maybe I understand its real idea or maybe not. I'd really appreciate someone experienced who can tell my if my assumptions are right or mean something.
Observation 1)
SPA - client requests www.example.com, this goes from browser to server. Server returns index.html which has just <div id="app"></div> and the script source for javascript file. Browser again makes another request for that bundled js file, server returns it. After that returned js file kicks in and starts executing. When compiled and done, the page is shown to the user.
SSR - client requests www.example.com from browser to server. Server does all the things, making any api calls or other things, puts everything in html and returns html. if these html has some styles or other js sources, browser will make requests for these too.
What I think - Why is SSR faster? Is it because in the SPA case, it had to download the whole js file of the whole website ? and in the SSR case, only the content of that specific page user is entering gets returned ?
Observation 2)
SPA - if the page got loaded and user clicks on one of other routes, it won't make any request to the server to get the html to show it to the user. all the route's js are already downloaded so no need to make request to the server unless there's an Ajax call for some dynamic data from database.
SSR - this would again make the request to the server to get the html file for the new page.
What I think SPA is faster in this case, even though SPA will still need to make the ajax request for some data. Ajax request for some data seem to be faster than the request to download the newly rendered html which would also need that ajax call on the server.
I know SSR is good for SEO, but i am only interested about performance. What do you think? Everything correct about what I said?
SPA
Load once, navigate freely. Sounds great, in theory at least. The initial load time can be a burden. If you have 6 different screens in your application, each taking .5s to load. Would you rather wait .5s everytime you get to a new one, or the initial 3s ? For most user any long loading time is unacceptable, so it's often better to load things incrementally.
On top of that, you often have the burden of actually running the framework to create pages inside the client browser for most modern JS framework (angular, vuejs, reactjs, etc...). That can create performance issues in some cases.
SSR
Generate everything server side, serve content dynamically. Sounds better for code splitting and letting the user load only what he needs. On top of that you can run your framework on a powerful server rather than the client computer/phone/fridge.
However, as you stated, you need to request more content more often. To avoid that, most modern framework are able to create partial routes, dynamically loading page fragments inside a fixed layout if only part of the page need to update when routing.
But that's not all, introducing
Service worker
Service workers and SSR are working very well together. If you establish a cache first strategy, that means that every time your user goes from page A to B to A again, you'll load applications fragment for A and B only once. You service worker will recognize that you are using the same fragment again and pull it from the user cache rather than making a new network request.
It makes things feel lightning fast.
On top of that you can also preload content. A user if hovering a link ? Start loading the app fragments used in this route. It might only save a few hundred ms, but when you are loading small apps fragments it can feel instantaneous for your user.
What's the catch then ? Well first of all it can actually be worse if you have a static application. Cache exists also for SPA, and if your app is small enough you might not care about the few ms saved during the initial loading time. Even with a larger application, it's mostly dependent on your user base.
But mostly, SSR requires an actual server to run the app. A lot of hosting services, like firebase or aws, allow you to host static files like you could use for a PWA, as well as handle a database easily from the client side, for a really cheap, if not free, cost. SSR will require a server, so you will have an increased running cost as those services.
In regards to your second observation, an SPA doesn't have to download everything up front . Through dynamic import an SPA can download assets on demand when a user switches route. This is an optimization to prevent the overhead of downloading everything up front.
As far as SSR vs SPA, SSR generally gives a better user experience. The server can send ready to be rendered HTML through SSR, so the application is mostly making dispatches and rendering the results with minimal client-side logic. If an SPA is designed with a framework, most assets downloaded will have to be processed prior to rendering. However, an SPA can be designed such that the minimal amount of code is provided on initial load and then all remaining assets are downloaded in the background.
You can do an awful lot with an SPA, but having the ability to offload processing to a dedicated server is almost always a win. However, if you are worried about user bandwidth or if you are developing a mobile application then an SPA might be worth considering. An SSR app can still do well here, but if you can get the assets onto the user device up front then you should have less latency-related issues.
Again everything comes down to design decisions, but from my experience an SSR application is more performant and scalable.
SSR provides the end user with a perceived performance improvement. If you are using SSR and SPA together, then SSR will give the end user something to look at quickly until the SPA is booted and the page is ready to be interacted with.

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

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

Node JS REST API, now how do I serve HTML files?

So I did a REST Api in Node JS and now I have a blocker.
app.use('/api', router);
This code makes sure that every url is prefixed with api. Now what If I want to serve an HTML file when the path is just "/"? Should I create another "express" application? I'm just working in localhost right now and I have to do "node server.js" to launch the server. I can't really launch 2 servers at the same time, or can I? One for the api: server.js and one for the rest of the code: client.js (that's a bad name but whatever.). I'm confused on how I have to setup things...
Thanks you for the help.
You can see that you use your api routes to, most likely, serve JSON content. Well, using the same mechanism you can configure your router to serve any other kind of content for a particular route.
So, if you would like to serve some HTML content for the root of your application, let's say a Wiki page or documentation of you API, it is as simple as:
app.get('/', function(req, res){
res.render('index.html');
});
Of course, if you are just rendering a static html page, you might just as well configure a middleware to define where you place all your static content.
app.use(express.static(path.join(__dirname, 'www')));
Where www is the directory where you chose to put your index.html page.
Having said that, if what you built was a REST API, chances are that it is intended to be reused by multiple other applications, and therefore, it is customary to that the client applications are running independently of the REST API. Some client applications may not even be Web applications (i.e. Android, iOS, desktop apps, etc). So you should take that into account before thinking in developing a Web client within the same project.
But nothing prevents your from providing a default implementation of UI that consumes your REST API within the same project/server. And yes, it is possible to run more than one HTTP server serving different applications. There are considerations you might need to take into account if you use separate servers for your API (i.e. CORS).
If you decide to serve everything within the same application, you may want to make sure there is a clear decoupling in the design in a such a way that the client application consumes and uses the rest layer as if it was independent. You should isolate the routes for the REST layer from those used for your client in such a way that if, later on, you want to make the client APP run independently you would not have a problem with that.
How about something like express-namespace to help you organize your routes?

Categories

Resources