November 06, 2019

Setting Up Angular Server-Side Rendering (SSR)

by Ahmed Bouchefra

Setting Up Angular Server-Side Rendering (SSR)

In this article, we'll learn about server-side rendering in Angular. We'll first see why you would need to add server-side rendering to your client-side application and then we'll proceed to add it to an example application

Let's get started!

Why Do We Need Server-side Rendering?

Angular applications are client-side applications that execute on the browser - which means they are rendered on the client, not on the server. Thanks to Angular Universal, you can add server-side rendering to your app. But why would you need to do that?

There are two main reasons to create a server-side version of your application:

  • Performance: Rendering Angular on the server-side improves the performance of your application, particularly on mobile and low-powered devices since the browser will not need extra time to render content which reduces the time for the First-contentful Paint
  • SEO: Server rendering allows search engine crawlers to easily crawl your web app which helps with Search Engine Optimization

Google can index and render JavaScript apps but not all engines are as advanced as Google. Also, social networks such as Facebook only understand server-rendered HTML. You can easily notice this when trying to share your client-side Angular app on social networks.

So, to make sure that every search engine and social network out there can recognize the content of your Angular web app, you need to implement the old server-side rendering or create what we call Universal apps - which are client-side JavaScript apps with server-side rendering.

What is Angular Universal?

Angular Universal is a technology that takes care of rendering Angular applications on the server. It runs on the server-side and generates static pages that are sent to the client browser which allows the application to render more quickly, giving users a chance to view the application layout before it becomes fully interactive.

Prerequisites

We'll see by example how to enable server-side rendering in Angular applications, so you will need to meet a few prerequisites in order to follow this tutorial comfortably:

  • You need to have a development environment with Node.js and NPM installed. You can install both of them from the downloads page of the official website. You can also refer to your system docs for the specific instructions on how to install Node on your system. A better way is to use NVM that allows you to install and manage multiple versions.
  • Familiarity with TypeScript.
  • Working knowledge of Angular and familiarity with the basic concepts.

Installing Angular CLI and Creating a New Project

Let's now install Angular CLI and initialize a new project.

Open a new terminal and run the following commands:

npm install -g @angular/cli
ng new angular-universal-example
cd angular-universal-example
ng serve

Your application will be available from http://localhost:4200.

Check out this tutorial for more information.

Setting up Angular Universal in Your Project

After creating your Angular project, let's now see how to add and set up Angular Universal.

You can easily add support for server-side rendering in your app using the Angular CLI thanks to the @nguniversal/express-engine schematic which automatically makes the required steps on behalf of you, as we'll see next.

Head back to a new command-line interface and run the following commands:

cd ~/angular-universal-example
ng add @nguniversal/express-engine --clientProject angular-universal-example

This command will install the required packages and add various files required for setting up SSR:

  • src/main.server.ts,
  • src/app/app.server.module.ts,
  • tsconfig.server.json,
  • webpack.server.config.js,
  • server.ts,

It will also update the following files accordingly:

  • package.json,
  • angular.json,
  • src/main.ts,
  • src/app/app.module.ts

The command will also spin up an Express server for you. Open the server.ts file; you should see the following code:

import 'zone.js/dist/zone-node';

import * as express from 'express';
import {join} from 'path';

// Express server
const app = express();

const PORT = process.env.PORT || 4000;
const DIST_FOLDER = join(process.cwd(), 'dist/browser');

// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const {AppServerModuleNgFactory, LAZY_MODULE_MAP, ngExpressEngine, provideModuleMap} = require('./dist/server/main');

// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
app.engine('html', ngExpressEngine({
  bootstrap: AppServerModuleNgFactory,
  providers: [
    provideModuleMap(LAZY_MODULE_MAP)
  ]
}));

app.set('view engine', 'html');
app.set('views', DIST_FOLDER);

// Example Express Rest API endpoints
// app.get('/api/**', (req, res) => { });
// Serve static files from /browser
app.get('*.*', express.static(DIST_FOLDER, {
  maxAge: '1y'
}));

// All regular routes use the Universal engine
app.get('*', (req, res) => {
  res.render('index', { req });
});

// Start up the Node server
app.listen(PORT, () => {
  console.log(`Node Express server listening on http://localhost:${PORT}`);
});

Protect your Angular App with Jscrambler

As you can see, in this file, we have a typical Express server with the view engine set to html and the views folder set to the dist/browser folder that contains the client-side bundle and which is created when the app is built. The server listens by default on the 4000 port and intercepts the possible requests for fetching HTML and static files.

Universal applications make use of the @angular/platform-server
package instead of @angular/platform-browser, which allows you to deliver Angular apps from the server.

In the Express server, requests from the client for fetching application pages are handled by the ngExpressEngine. This simply works by calling the renderModuleFactory() function.

Building and Serving the App with Express Server

Thanks to Angular’s CLI, we've quickly setup Server-side rendering and created a Universal version of our application. The CLI has even created the Express server for us which takes care of compiling HTML pages with Angular Universal based on client requests.

Now, let's see how to use the Angular CLI to compile and bundle the Universal version of our application and run the Express server to serve the rendered Angular app.

Head back to your terminal, make sure you are inside your Angular project and run the following commands:

npm run build:ssr 
npm run serve:ssr

This will compile both the Angular app and the server app and will launch the Express server from the http://localhost:4000 address. Navigate to this address using your web browser; you should see your Angular app up and running:

Angular SSR

You should also see the same application served by the ng serve command from the http://localhost:4200 address but this is different as it's server-rendered and served by an Express server.

Conclusion

In this article, we've seen that Server-side rendering works by rendering a part of your client-side app on the server instead of the client.

This can help increase performance and allow all search engine crawlers and social media networks to easily crawl your application for SEO purposes.

We also used the Angular CLI and the @nguniversal/express-engine CLI schematic to quickly create a Universal version of our Angular application and prepare our app for server-side rendering.

A final step would be to make sure that our Angular app's source code will be protected against code theft, tampering, and reverse engineering. To achieve that, follow our guide.