Partial Server Side Rendering with Angular 9 and How to Deploy it
UPDATE: 10.4.2020, Angular 9
This tutorial follows the official instructions at Angular's Universal Guide delivering rendered code via the Node.js Express Framework . If you are using Nrwl Nx you should skip this section and follow the instructions at Nx workspace schematics: server side rendering (SSR) with Angular Universal instead.
Creating the Angular project with ssr capabilities
The first thing we do is creating a new Angular project using Angular's CLI and simply add Universal Support to it.
ng new frontend
cd frontend
ng add @nguniversal/express-engine --clientProject frontend
The command above added, among others, a source file
server.ts
at the top level of our Angular project. This source file is gonna be the entry point of the the Node Server that delivers our rendered pages.
We can now run
npm run build:ssr && npm run serve:ssr
to build the project and test our Angular project locally. When
browsing to
http://localhost:4200
and analyzing the network traffic, we can already see the magic:
The first response delivered by the server (after 20ms) does already include rendered Html without changing any source files. As soon as the bundled javascript files are loaded and interpreted Angular automatically replaces the content with browser rendered component code. You can test this behaviour by turning off Javascript in your Browser.
Add a page which should then be rendered by SSR
We now create a routed page on
/public/
displaying a component called
PublicPageComponent
. This component is going to be the only component that will be rendered on the server-side. It should either display
Server
or
Browser
, depending on who renders the component at the moment. We now create a routed page on
/public/
displaying a component called
PublicPageComponent
. This component is going to be the only component that will be rendered on the server-side. It should either display
Server
or
Browser
, depending on who renders the component at the moment. When SSR is active you notice a change from
Rendered by Server
to
Rendered by Browser
.
Therefore we create the component
cd src
ng g c PublicPage --module app
and add it to
app-routing.module.ts
:
const routes: Routes = [{ path: "public", component: PublicPageComponent }];
Finally we modify the component's code to show the current renderer:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import { Component, Inject, OnInit, PLATFORM_ID } from "@angular/core";
import { isPlatformBrowser } from "@angular/common";
@Component({
selector: "app-public-page",
template: "<p>Rendered by {{ renderer }}</p>",
styleUrls: ["./public-page.component.sass"]
})
export class PublicPageComponent {
renderer: string;
constructor(@Inject(PLATFORM_ID) platformId: any) {
this.renderer = isPlatformBrowser(platformId) ? "Browser" : "Server";
}
}
|
Setting up a nginx service which passes requests to node-js server
In order to render only some parts of our website on the server-side we need to have at least two services running: A webserver that acts like a proxy and responds to the client's browser and another one that is doing the rendering job on demand. For simplicity's sake the proxy will deliver everything but the SSR-Page. In production there will most likely be a proxy in front of everything to fulfil performance and security needs.
Our setting will look this:
Therefore we create a Nginx service that serves the compiled output files of our Angular website.
When running the command
npm run build:ssr
we will get compiled output files at:
frontend
+-- dist
+-- browser
... (static files, Angular`s compile output)
+-- server
main.js (Angular SSR bundle)
server.js (-> node.js express engine)
To set up the proxy service we simply create a Nginx instance and copy files from
frontend/dist/browser
into it's html root folder
(you can use
this Dockerfile
to set up a virtualized container).
In our case, the root folder would be
/usr/share/nginx/html
. Therefore we have to configure nginx to serve everything from
within this folder but forward
/public
HTTP requests to the Angular SSR Node Service.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
server {
listen 80;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html =404;
}
# Forward requests to the node service which
# renders on the server side:
location ~ ^/(public)$ {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://internal-hostname-of-ssr-server:4000;
}
include /etc/nginx/extra-conf.d/*.conf;
}
|
Note: If you need to extend the range of pages you want to render at the server-side you just have to adapt the regular expression checking the
location
.
Setting up a NodeJS Service for SSR
Finally we have to set up the Service that renders
/public
at the server-side. Nothing could be easier - we did it before:
By executing
npm run serve:ssr
the service starts up. If you have filename hashing enabled you have to make sure that you are using the exact same files as the nginx container uses. Therefore you must not execute
npm run build:ssr
again.
Complete project example using Docker Containers
The code above and a complete setting can be found at the following example repository: https://github.com/carrotandcompany/angular-partial-ssr-example .
Do you think Server Side Rendering can help your project and want some help setting it up?