Server Side Rendering With StrongLoop

Previously on The Ultimate Guide For Native App Development I explained a wonderful way to integrate MongoDB with the IBM StrongLoop’s LoopBack Framework, Angular 2 and NativeScript 2 to build truly Native Mobile Applications using the MEAN Stack.

Today I want you to forget about one of the biggest and annoying issues with Angular 1:

The SEO Nightmare Story.

Project Description

For this post I have used as reference PatrickJS’ Examples, he did a wonderful job integrating Angular Universal with different NodeJS Frameworks like ExperessJS and HapiJS, if you use one of these frameworks I suggest you to look at These Examples.

But I think is valuable to create an example for you to setup a really powerful MEAN Stack Environment by integrating Angular Universal with the IBM StrongLoop’s LoopBack Framework.

Technology Stack

  • MongoDB.- Building on the Best of Relational with the Innovations of NoSQL.
  • LoopBack.- The Node.js API Framework.
  • Angular 2.- Development platform for building mobile and desktop applications.
  • Angular Universal.- Server-side Rendering for Angular 2 apps.

Requirements

GitHub Example Code Repository

LoopBack Universal

Setup Environment

Ok since you already have installed the environment stack, now we need to install the platforms needed for this project, for this we are going to use NPM.

1
$ npm install -g strongloop angular-cli

DataBase

For this demo example does not make sense to setup a DB and a DataSource, but in real life you will want to setup these, so.. just read my previous tutorial and follow the DataBase and DataSource sections.

Setup REST API Project

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
$ mkdir loopback-universal
$ cd loopback-universal
$ slc loopback


_-----_
| | .--------------------------.
|--(o)--| | Let's create a LoopBack |
`---------´ | application! |
( _´U`_ ) '--------------------------'
/___A___\
| ~ |
__'.___.'__
´ ` |° ´ Y `

? What's the name of your application? loopback-universal

? What kind of application do you have in mind? api-server (A LoopBack API server with local User auth)
Generating .yo-rc.json


I'm all done. Running npm install for you to install the required dependencies. If this fails, try running the command yourself.


create .editorconfig
create .jshintignore
create .jshintrc
create server/boot/root.js
create server/middleware.json
create server/middleware.production.json
create server/server.js
create README.md
create server/boot/authentication.js
create .gitignore
create client/README.md
loopback-universal@1.0.0 /Volumes/backup/development/www/mean.expert/examples/loopback-universal

Next steps:

Create a model in your app
$ slc loopback:model

Compose your API, run, deploy, profile, and monitor it with Arc
$ slc arc

Run the app
$ node .

We have just created a LoopBack Application, if you run the $ node . command, you will have a fully working RESTful API running in your machine/server that includes built in User Model, Authentication mechanism and API Explorer.

Modify Default Root MiddleWare

The LoopBack Application by default presents a status service that help for ping purposes, but since we now want to serve a NG2 Application we need to modify the server/boot/root.js as follows:

1
2
3
4
5
6
module.exports = function(server) {
// Install a `/` route that returns server status
var router = server.loopback.Router();
router.get('/ping', server.loopback.status());
server.use(router);
};

Basically what we are doing is to change the status middleware to an endpoint [GET] /ping, of course you could use [GET] /status or even just delete the server/boot/root.js file, it is up to you.

Setup Angular 2 App

Now that we have the RESTful API set we need to turn this project into a fullstack application.

1
2
$ rm -r client
$ ng new client

If you run the angular application using the following commands, you will see how we get the html without being fully rendered (as usual):

Front End Rendering

We can verify because <client-app>Loading....</client-app> within the mark up does not content client works!.

Setup Angular Universal With StrongLoop

Kill the app process and then install the Angular Universal Module.

1
2
$ cd client
$ npm install --save preboot angular2-universal @angular/platform-server @angular/router-deprecated

Once these modules are installed, create a new file client/universal.js with the following content:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
"use strict";
/**
* UNIVERSAL DEPENDENCIES
*/

require('angular2-universal/polyfills');
var NG2Universal = require('angular2-universal');
var client = require('./dist/app');
var server = require('../server/server');
var path = require('path');
/**
* Enable Prod Mode
*/

NG2Universal.enableProdMode();
/**
* REQUIRED PROPERTIES
*/

var baseUrl = '/';
/**
* LOG LOOBACK UNIVERSAL STARTER
*/

console.log('\nANGULAR UNIVERSAL: SERVER SIDE RENDERING WITH STRONGLOOP\n');
/**
* SERVER CONFIGURATIONS
*/

server.engine('.html', NG2Universal.expressEngine);
server.set('views', __dirname + '/dist');
server.set('view engine', 'html');
server.set('view options', { doctype: 'html' });
server.use(server.loopback.static(path.join(__dirname, 'dist'), { index: false }));
server.use(server.loopback.static(path.join(__dirname, 'src'), { index: false }));
server.use(baseUrl, function ngApp(req, res) {
var url = req.originalUrl || baseUrl;
var config = {
directives: [client.ClientAppComponent],
platformProviders: [
NG2Universal.provide(NG2Universal.ORIGIN_URL, { useValue: 'http://localhost:' + server.get('port') }),
NG2Universal.provide(NG2Universal.BASE_URL, { useValue: baseUrl }),
],
providers: [
NG2Universal.provide(NG2Universal.REQUEST_URL, { useValue: url }),
NG2Universal.NODE_ROUTER_PROVIDERS,
NG2Universal.NODE_HTTP_PROVIDERS,
],
async: true,
preboot: { appRoot: 'client-app' }
};
res.render('index', config);
});
/**
* START ANGULAR UNIVERSAL: SERVER SIDE RENDERING WITH STRONGLOOP
*/

server.start();

Since LoopBack is built on top of ExpressJS the configuration of universal will be similar with some differences in order to correctly integrate these platforms.

Update Client Component

Before we test and run the server using universal, we need to update the component in order to set the correct path within the @Component Decorator.

1
2
3
4
5
6
7
8
9
10
11
import { Component } from '@angular/core';

@Component({
moduleId: module.id,
selector: 'client-app',
templateUrl: '/app/client.component.html',
styleUrls: ['/app/client.component.css']
})
export class ClientAppComponent {
title = 'client works!';
}

Configure the main package.json file

Modify the main package.json loopback-universal/package.json as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
"name": "loopback-universal",
"version": "1.0.0",
"main": "client/universal.js",
"scripts": {
"start": "npm run build:app && node client/universal",
"pretest": "jshint .",
"posttest": "nsp check",
"build:app": "cd client && ng build"
},
"dependencies": {
"compression": "^1.0.3",
"cors": "^2.5.2",
"helmet": "^1.3.0",
"loopback": "^2.22.0",
"loopback-boot": "^2.6.5",
"loopback-component-explorer": "^2.4.0",
"loopback-datasource-juggler": "^2.39.0",
"serve-favicon": "^2.0.1"
},
"devDependencies": {
"jshint": "^2.5.6",
"nsp": "^2.1.0",
"loopback-sdk-angular": "github:jonathan-casarrubias/loopback-sdk-angular",
"loopback-sdk-angular-cli": "github:qeti/loopback-sdk-angular-cli"
},
"repository": {
"type": "",
"url": ""
},
"license": "MIT",
"description": "loopback-universal"
}

Awesome, now we have integrated our LoopBack Application with Angular Universal, so lets test by running the following command:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
loopback-universal$ npm run start

> loopback-universal@1.0.0 start /Volumes/backup/development/www/mean.expert/examples/loopback-universal
> npm run build:app && node client/universal


> loopback-universal@1.0.0 build:app /Volumes/backup/development/www/mean.expert/examples/loopback-universal
> cd client && ng build

Visit http://ember-cli.com/user-guide/#watchman for more info.
Built project successfully. Stored in "dist/".
no original function chain to wrap

ANGULAR UNIVERSAL: SERVER SIDE RENDERING WITH STRONGLOOP

Web server listening at: http://0.0.0.0:3000
Browse your REST API at http://0.0.0.0:3000/explorer

If you open a browser and check the application, now we get a server side rendered view at least for the first time you request a section, later will continue working with all the magic we expect from Angular 2.

Server Side Rendering

Amazing!!! Finally we are able to say goodbye to our seo problems for those who use use LoopBack as Back End Framework.

What is next?

In my next blog posts I want to explore more about Angular 2 and its ecosystem, also I will keep showing more about IBM’s StrongLoop LoopBack Framework and of course NativeScript.

If you like this series and want be aware of next releases and new packages, follow me on Twitter @johncasarrubias and if you feel it leave a comment here.

Thanks for reading.


Comments:

...