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

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?

Related

Setting host and port when executing a .jar

I have an Angular + Spring Boot app that's deployed as a single .jar file, some .properties files and a bash script to execute the .jar depending on what properties we want to use.
Right now, my .ts classes have host and port set statically, e.g.:
const PATH: string = 'http://localhost:8080/(...)';
Is there a way to set them dynamically while executing the .jar?
The question is very broad and could not be answered definitely with such minimal information. I want to point some things though which you must consider in what ever solution you apply to this problem.
The backend which is a spring boot app, may as well run in localhost:8080 and that is fine. It is never however the case that an application backend is in the same machine as the Frontend. You have adopted angular as your frontend which means by default (if you haven't overridden this behavior) some static files would be served and executed in the browser of the end user.
So this in most cases means that the end user must know the ip-address or domain so that he can reach the machine that runs the backend server.
So the end situation you will have is backend running in for example localhost:8080 in some VM and frontend using a property like const PATH: string = 'http://my-backend-domain.com:8080/(...)'; so that an external user can reach the backend server from his browser.
In a very simplified scenario an effective solution would be to adapt your angular app with this answer then during deployment decide what is the domain for example where you are deploying the app like /my-backend-domain.com and go to assets/data/config.json and configure it there. This would not require any additional build of Frontend just a simple configuration.
There are many ways to do it
environmental variables
application arguments CommandLineRunner
configuration file

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.

Parametrization of Angular application

I have an application that is comprised of an Angular front-end, an app layer and a DB layer. You can see the architecture in this image.
I am using an nginx instance to serve both the JS front-end bits to the client as well as to proxy requests from the client to the app layer. So let's say I deploy this nginx on a cloud VM with IP 18.1.1.1 (fake) I can point my browser to that IP, the client will download the JS code, and the JS code is configured, see here, to set the app server ip/fqdn to the same ip/fqdn I pointed my browser to download the ui.
At this point the nginx proxy configuration kicks in and forward all /api requests made by the JS code to a specific fqdn. Right now this is a specific FQDN just because I am deploying these components as containers and the nginx container always knows how to reach http://yelb-appserver:4567/api.
I would like now to create additional deployment methods and in particular I would like to host the Angular bits on an S3 bucket (or any other web server) and have the JS point directly to something like an API GW, a separate EC2 instance, a cloud load balancer, or anything that represents an IP/FQDN endpoint different from the IP/FQDN of the web server serving the JS files.
In this case I can no longer use the appserver_env: 'http://' + window.location.host that I have used here.
Since I would like to create a dynamic and repeatable deployment workflow (using cloudformation, or similar) I am wondering if there is a way to work with a single JS compiled artifact parametrizing the Angular code to point to the /api endpoint created at deployment time OR if my only option is, at every deployment, to 1) create/read the /api endpoint at deployment time, 2) programmatically customize the Angular code with the endpoint, 3) re-build the Angular app dynamically (now including the specific /api endpoint) and 4) finally deploy the web site with the JS code ad-hoc created with the custom /api endpoint for that specific application instance deployed.
Thanks.
Use environment variables and keep them in a config (like "environment.prod.ts" in your case), which will be given to a node process running your build. Your javascript angular code can use these variables, like for api endpoint, you can have process.env.API_ENDPOINT in you code wherever you need api endpoint. Now for supplying thses variables you can use something, as simple as, API_ENDPOINT='/api' npm run build or for more advanced approach, you can use Docker.

Why do we need Express server when we have a backend ready already

I am quite new to javascript and web application environment. I have seen a react web application project which had a public directory, a client directory and a server directory. I have few question
Why do we need an express server file setup in the frontend project if we already have backend APIs ready and backend server ready
Do we need an express server if we make the frontend on react and call the APIs to fetch the data for application.
Isn't the backend server and express server in the frontend project are same?
Why do we need an express server file setup in the frontend project if we already have backend APIs ready and backend server ready
You don't.
You need an HTTP server to listen for and respond to any Ajax requests you make from your client side code.
You need an HTTP server to listen for and respond to any requests for the HTML documents and static resources (JS, CSS, images, etc) that your pages need.
These can be the same HTTP server, different HTTP servers, written with Express or not written with Express.
React tutorials tend to ignore mentioning this and just dive in showing how to use Express for everything. Don't read too much into that.
Do we need an express server if we make the frontend on react and call the APIs to fetch the data for application.
No. See above.
Isn't the backend server and express server in the frontend project are same?
Maybe. It is up to you. See above.
There is no such thing as a "backend server" and a "frontend server", a simple web application is composed of two main parts:
1/ an application that serves html pages, which runs on a backend, so it is usually called a server, but a typical cloud server nowadays can run hundreds of different serving apps at the same time
2/ a frontend, which is typically a complex piece of JavaScript software and html pages that are dynamically send to the user browser and execute locally
The minimum that you require to have a working website is a server application that will return one or several html pages upon user request. A typical React + Node project is organized as follow:
A server directory: which contains all the code for the serving app - the one returning the webpages, it can also contains code that handle the REST API, in case your client app requires dynamic data or if your server connect to a database. Note that the webpage server and the API server could be two different- or more - applications.
You usually dont want to share to users your server code, so typically you have a public directory that contains the html pages and this is the only location on the disk - theoretically - that can be access by users. This directory can also contains required images and resources needed by the webpages, it is also called static resources
To keep thing more organized, the code of the frontend application is placed in a client directory but on production is usually bundled in one or few files, depending on the size of the app, and also placed in the public directory, so it contains everything needed to serve the app.
Hope it helps
We don't need an Express server, however, adding it comes with great benefits:
It helps add compression to an angular / react application that uses only in-memory server (if that is your case).
Defines base path where to serve static files for your project and can also add gzip compression headers for each of the request so that the server returns the compressed versions.
Helps you parse your API calls to the correct format expected by the UI so that the parsing logic stays in the express server and not the UI. This is helpful in the event that the API response changes in the future, or when the final backend endpoint changes, no need to modify the UI, but the route in the express server.
I have found out these and other benefits while looking how to add compression to an angular application (turns out you cannot without express or an actual web server).

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

Categories

Resources