Skip to content

Commit

Permalink
feat(examples): add support for server side rendering with universal
Browse files Browse the repository at this point in the history
  • Loading branch information
flolu authored and alexeagle committed Mar 31, 2020
1 parent dfd9aea commit c09ca89
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 5 deletions.
14 changes: 12 additions & 2 deletions examples/angular/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ This example is a monorepo, meant to show many different features and integratio
- **Lazy loading**: in production mode, the application is served in chunks. Run `ng serve --prod`
- **Differential loading**: in production mode, we load a pair of `<script>` tags. Modern browsers will load code in the ES2015 syntax, which is smaller and requires fewer polyfills. Older browsers will load ES5 syntax.
- **Docker**: see below where we package up the production app for deployment on Kubernetes.
- **Server Side Rendering**: with the help of Angular Universal you can render your application on the server

## Installation

Expand Down Expand Up @@ -97,6 +98,14 @@ $ ng serve --prod
$ bazel run //src:prodserver
```

You can also use server side rendering.

```bash
$ yarn server-ssr
# or
$ bazel run //src:universal_server
```

### Code splitting

The production bundle is code split and routes such as `/` and `/todos`
Expand Down Expand Up @@ -128,6 +137,7 @@ We use the standard firebase deploy command.
Run `yarn deploy` to release changes to bazel.angular.io.

### Kubernetes Engine

We use Bazel's docker support to package up our production server for deployment.
Each time the app changes, we'll get a slim new docker layer with just the modified files, keeping the round-trip for deployment incremental and fast.
This example is configured to run on Google Kubernetes Engine, so we can have an elastic pool of backend machines behind a load balancer.
Expand All @@ -146,9 +156,9 @@ Deploy to production:

1. Install gcloud and kubectl
1. Authenticate to the Google Container Registry
`gcloud auth configure-docker`
`gcloud auth configure-docker`
1. Authenticate to Kubernetes Engine
`gcloud container clusters get-credentials angular-bazel-example --zone=us-west1-a`
`gcloud container clusters get-credentials angular-bazel-example --zone=us-west1-a`
1. For the first deployment: `bazel run :deploy.create`
1. To update: `bazel run :deploy.replace`

Expand Down
3 changes: 3 additions & 0 deletions examples/angular/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
"@angular/material": "9.0.0",
"@angular/platform-browser": "9.0.0",
"@angular/platform-browser-dynamic": "9.0.0",
"@angular/platform-server": "^9.0.0",
"@angular/router": "9.0.0",
"@ngrx/store": "9.0.0-beta.0",
"@nguniversal/express-engine": "^9.0.0",
"date-fns": "1.30.1",
"rxjs": "6.5.3",
"systemjs": "6.1.2",
Expand Down Expand Up @@ -66,6 +68,7 @@
"serve": "ibazel run //src:devserver",
"deploy": "ng build && firebase deploy",
"serve-prod": "bazelisk run //src:prodserver",
"serve-ssr": "bazelisk run //src:universal_server",
"e2e": "bazelisk test //e2e:all",
"test": "bazelisk test //src/...",
"benchmark": "ibazel-benchmark-runner //src:devserver src/app/hello-world/hello-world.component.ts --url=http://localhost:5432",
Expand Down
30 changes: 29 additions & 1 deletion examples/angular/src/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
load("@build_bazel_rules_nodejs//:index.bzl", "pkg_web")
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary", "pkg_web")
load("@io_bazel_rules_docker//container:container.bzl", "container_image")
load("@io_bazel_rules_docker//nodejs:image.bzl", "nodejs_image")
load("@io_bazel_rules_sass//:defs.bzl", "sass_binary")
Expand All @@ -18,6 +18,12 @@ ts_config(
deps = [":tsconfig.json"],
)

ts_config(
name = "tsconfig-server",
src = "tsconfig.server.json",
deps = [":tsconfig.json"],
)

# Run the sass compiler to output "styles.css"
# TODO(alexeagle): demonstrate the sass_library rule too
sass_binary(
Expand Down Expand Up @@ -232,3 +238,25 @@ container_image(
tags = ["local"],
workdir = "/app/src/nodejs_image.binary.runfiles/examples_angular",
)

ts_library(
name = "universal_server_lib",
srcs = [
"server.ts",
],
deps = [
"//src/app:app_server",
"@npm//@nguniversal/express-engine",
"@npm//@types/node",
"@npm//express",
],
)

nodejs_binary(
name = "universal_server",
data = [
":prodapp",
":universal_server_lib",
],
entry_point = ":server.ts",
)
15 changes: 14 additions & 1 deletion examples/angular/src/app/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ package(default_visibility = ["//:__subpackages__"])

ng_module(
name = "app",
srcs = glob(["*.ts"]),
srcs = glob(
include = ["*.ts"],
exclude = ["app.server.module.ts"],
),
assets = ["app.component.html"],
tsconfig = "//src:tsconfig.json",
deps = [
Expand All @@ -18,3 +21,13 @@ ng_module(
"@npm//@ngrx/store",
],
)

ng_module(
name = "app_server",
srcs = ["app.server.module.ts"],
tsconfig = "//src:tsconfig-server",
deps = [
":app",
"@npm//@angular/platform-server",
],
)
3 changes: 2 additions & 1 deletion examples/angular/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import {todoReducer} from './todos/reducers/reducers';
declarations: [AppComponent],
imports: [
AppRoutingModule, BrowserModule, BrowserAnimationsModule, MaterialModule, HomeModule,
StoreModule.forRoot({todoReducer})
StoreModule.forRoot({todoReducer}),
BrowserModule.withServerTransition({ appId: 'angular-bazel-example' })
],
exports: [AppComponent],
bootstrap: [AppComponent],
Expand Down
11 changes: 11 additions & 0 deletions examples/angular/src/app/app.server.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {NgModule} from '@angular/core';
import {ServerModule} from '@angular/platform-server';

import {AppModule} from './app.module';
import {AppComponent} from './app.component';

@NgModule({
imports: [AppModule, ServerModule],
bootstrap: [AppComponent]
})
export class AppServerModule {}
41 changes: 41 additions & 0 deletions examples/angular/src/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
///<reference types="node"/>
import "zone.js/dist/zone-node";

import { ngExpressEngine } from "@nguniversal/express-engine";
import * as express from "express";
import { join } from "path";

const app = express();

const PORT = process.env.PORT || 4000;
const DIST_FOLDER = join(process.cwd(), "src/prodapp");

import { AppServerModule } from "./app/app.server.module";

app.engine(
"html",
ngExpressEngine({
bootstrap: AppServerModule,
providers: [
// TODO add support for lazy loading with server side rendering
// provideModuleMap(LAZY_MODULE_MAP)
]
}) as any
);

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

app.get("*.*", express.static(DIST_FOLDER, { maxAge: "1y" }));

// catch /favicon.ico route to prevent the following server error:
// Error: Cannot match any routes. URL Segment: 'favicon.ico'
app.get("/favicon.ico", (req, res) => res.send(""));

app.get("*", (req, res) => {
res.render("example/index", { req });
});

app.listen(PORT, () => {
console.log(`Node Express server listening on http://localhost:${PORT}`);
});
12 changes: 12 additions & 0 deletions examples/angular/src/tsconfig.server.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app-server",
"module": "commonjs",
"types": ["node"]
},
"files": ["src/main.server.ts", "server.ts"],
"angularCompilerOptions": {
"entryModule": "./src/app/app.server.module#AppServerModule"
}
}
30 changes: 30 additions & 0 deletions examples/angular/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,14 @@
resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-9.0.0.tgz#b9454f29d8edaf024668baa9e07083eef73deac2"
integrity sha512-2PR/o57HjZvKEnAF8ODeqxmeC90oth9dLTMrJNoI5MET0IeErKeI/9Sl5cLQuXC+lSVN5rOMCvDb74VWSno5yw==

"@angular/platform-server@^9.0.0":
version "9.1.0"
resolved "https://registry.yarnpkg.com/@angular/platform-server/-/platform-server-9.1.0.tgz#a4d5b20724acd54219603e32cee88b0b58ee6617"
integrity sha512-JboTIUBgf9yHRjF93q8NJQoeIp5Zp4kg8Nmw1QLWO6SV+OpiOpKSrlScMVncCh0KiU6SIOmiuHJwBhLm78fi+Q==
dependencies:
domino "^2.1.2"
xhr2 "^0.1.4"

"@angular/router@9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@angular/router/-/router-9.0.0.tgz#11784fc8ce9cb3314c7ec1083ff9be7c611181c2"
Expand Down Expand Up @@ -901,6 +909,18 @@
resolved "https://registry.yarnpkg.com/@ngrx/store/-/store-9.0.0-beta.0.tgz#b352f0394b1b652ee2ae2039ba1757a704290846"
integrity sha512-yfw+Qwiu3fNjjRD1B2ZSGpjd3a70fLjFfqhu2H636giia5cTnqbzIqOH3EnZzapADzwL6M7yy7tixWpJ24lbpQ==

"@nguniversal/common@9.1.0":
version "9.1.0"
resolved "https://registry.yarnpkg.com/@nguniversal/common/-/common-9.1.0.tgz#01ac2d2c47c6d04f955f1713697377afcaa1ad91"
integrity sha512-Pvb3KhuV44PxLmVOf1dqnuckdaNdfT5tbiUu2/vVbdtyFdQpF40D1Zx4RumRymK0ZzTJGQsJtJSi2DJvvGgwMg==

"@nguniversal/express-engine@^9.0.0":
version "9.1.0"
resolved "https://registry.yarnpkg.com/@nguniversal/express-engine/-/express-engine-9.1.0.tgz#ddc1cecb4134a365142a9ba6ebb6ae8f99cb2ad1"
integrity sha512-uRb8dJD3huk42eY1iOpmr8yfq/LTSOoUfKY4eVREE7nMePyZNSRQHNCEybHDTNh43FvTN38u+cA4dTlmMFD5GQ==
dependencies:
"@nguniversal/common" "9.1.0"

"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf"
Expand Down Expand Up @@ -2407,6 +2427,11 @@ dom-serialize@^2.2.0:
extend "^3.0.0"
void-elements "^2.0.0"

domino@^2.1.2:
version "2.1.4"
resolved "https://registry.yarnpkg.com/domino/-/domino-2.1.4.tgz#78922e7fab7c610f35792b6c745b7962d342e9c4"
integrity sha512-l70mlQ7IjPKC8kT7GljQXJZmt5OqFL+RE91ik5y5WWQtsd9wP8R7gpFnNu96fK5MqAAZRXfLLsnzKtkty5fWGQ==

dot-prop@^4.1.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57"
Expand Down Expand Up @@ -7280,6 +7305,11 @@ xdg-basedir@^3.0.0:
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=

xhr2@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/xhr2/-/xhr2-0.1.4.tgz#7f87658847716db5026323812f818cadab387a5f"
integrity sha1-f4dliEdxbbUCYyOBL4GMras4el8=

xml2js@^0.4.17:
version "0.4.22"
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.22.tgz#4fa2d846ec803237de86f30aa9b5f70b6600de02"
Expand Down

0 comments on commit c09ca89

Please sign in to comment.