Yanick Dickbauer
Software Developer at Carrot & Company. Writes about tech topics.
13 Minutes
Article posted on November 11, 2019
Partial Server Side Rendering with Angular 8 and How to Deploy it
13 Minutes
Article posted on November 11, 2019
With a slow connection the first page load can take forever. Server-Side Rendering can help. How to set it up, when not to do it and to combine it with the advantages of Single Page Applications.

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:

First page

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. When SSR is active you notice a change from Rendered by Server to _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:

Setting of our services

Therefore we create a Nginx service that serves the compiled output files of our Angular website.

When running the command

npm run build:ssr -- --configuration=production

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-ssr/.

Do you think Server Side Rendering can help your project and want some help setting it up?