Skip to content

Commit a508ba8

Browse files
codediodeiojamesdaniels
authored andcommitted
docs(): guide for Angular Universal SSR (angular#1686)
1 parent 26f7613 commit a508ba8

File tree

1 file changed

+154
-0
lines changed

1 file changed

+154
-0
lines changed

docs/server-side-rendering.md

+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# Server-side Rendering with Universal
2+
3+
Server-side rendering (SSR) is the process of converting a JavaScript app to plain HTML at request-time, allowing search engine crawlers and linkbots to understand page content reliably.
4+
5+
## 0. Prerequisites
6+
7+
- @angular/cli >= v6.0
8+
- angularfire2 >= v5.0.0-rc.7
9+
10+
## 1. Generate the Angular Universal Server Module
11+
12+
First, create a server module with the Angular CLI.
13+
14+
```
15+
ng generate universal --client-project <your-project>
16+
```
17+
18+
## 2. Build a Server with ExpressJS
19+
20+
[ExpressJS](https://expressjs.com/) is a lightweight web framework that can serve http requests in Node. First, install the dev dependencies:
21+
22+
```
23+
npm install --save-dev express webpack-cli ts-loader ws xmlhttprequest
24+
```
25+
26+
Create a file called `server.ts` in the root of you project.
27+
28+
```ts
29+
// These are important and needed before anything else
30+
import 'zone.js/dist/zone-node';
31+
import 'reflect-metadata';
32+
33+
import { renderModuleFactory } from '@angular/platform-server';
34+
import { enableProdMode } from '@angular/core';
35+
36+
import * as express from 'express';
37+
import { join } from 'path';
38+
import { readFileSync } from 'fs';
39+
40+
// Required for Firebase
41+
(global as any).WebSocket = require('ws');
42+
(global as any).XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;
43+
44+
45+
// Faster renders in prod mode
46+
enableProdMode();
47+
48+
// Express server
49+
const app = express();
50+
51+
const PORT = process.env.PORT || 4000;
52+
const DIST_FOLDER = join(process.cwd(), 'dist');
53+
const APP_NAME = 'YOUR_PROJECT_NAME';
54+
55+
const { AppServerModuleNgFactory } = require(`./dist/${APP_NAME}-server/main`);
56+
57+
// index.html template
58+
const template = readFileSync(join(DIST_FOLDER, APP_NAME, 'index.html')).toString();
59+
60+
app.engine('html', (_, options, callback) => {
61+
renderModuleFactory(AppServerModuleNgFactory, {
62+
document: template,
63+
url: options.req.url,
64+
}).then(html => {
65+
callback(null, html);
66+
});
67+
});
68+
69+
app.set('view engine', 'html');
70+
app.set('views', join(DIST_FOLDER, APP_NAME));
71+
72+
// Serve static files
73+
app.get('*.*', express.static(join(DIST_FOLDER, APP_NAME)));
74+
75+
// All regular routes use the Universal engine
76+
app.get('*', (req, res) => {
77+
res.render(join(DIST_FOLDER, APP_NAME, 'index.html'), { req });
78+
});
79+
80+
// Start up the Node server
81+
app.listen(PORT, () => {
82+
console.log(`Node server listening on http://localhost:${PORT}`);
83+
});
84+
```
85+
86+
## 3. Add a Webpack Config for the Express Server
87+
88+
Create a new file named `webpack.server.config.js` to bundle the express app from previous step.
89+
90+
91+
```js
92+
const path = require('path');
93+
const webpack = require('webpack');
94+
95+
const APP_NAME = 'YOUR_PROJECT_NAME';
96+
97+
module.exports = {
98+
entry: { server: './server.ts' },
99+
resolve: { extensions: ['.js', '.ts'] },
100+
mode: 'development',
101+
target: 'node',
102+
externals: [/(node_modules|main\..*\.js)/],
103+
output: {
104+
path: path.join(__dirname, `dist/${APP_NAME}`),
105+
filename: '[name].js'
106+
},
107+
module: {
108+
rules: [
109+
{ test: /\.ts$/, loader: 'ts-loader' }
110+
]
111+
},
112+
plugins: [
113+
new webpack.ContextReplacementPlugin(
114+
/(.+)?angular(\\|\/)core(.+)?/,
115+
path.join(__dirname, 'src'), // location of your src
116+
{} // a map of your routes
117+
),
118+
new webpack.ContextReplacementPlugin(
119+
/(.+)?express(\\|\/)(.+)?/,
120+
path.join(__dirname, 'src'),
121+
{}
122+
)
123+
]
124+
}
125+
```
126+
127+
## 4.0 Build Scripts
128+
129+
Update your `package.json` with the following build scripts.
130+
131+
```js
132+
"scripts": {
133+
// ... omitted
134+
"build:ssr": "ng build --prod && ng run YOUR_PROJECT_NAME:server && npm run webpack:ssr",
135+
"serve:ssr": "node dist/YOUR_PROJECT_NAME/server.js",
136+
"webpack:ssr": "webpack --config webpack.server.config.js"
137+
},
138+
```
139+
140+
Test your app locally by running `npm run build:ssr && npm run serve:ssr`.
141+
142+
## 5.0 Deployment
143+
144+
With an existing Firebase project, you can easily deploy your ExpressJS server to [App Engine Flex](https://cloud.google.com/appengine/docs/flexible/) (Note: This is a paid service based on resource allocation).
145+
146+
147+
1. Install [gcloud CLI tools](https://cloud.google.com/sdk/gcloud/) and authenticate.
148+
2. Change the start script in package.json to `"start": "npm run serve:ssr"`
149+
2. Run `gcloud app deploy` and you're on the cloud.
150+
151+
## Additional Resources
152+
153+
- [Universal Starter Template](https://github.com/angular/universal-starter)
154+
- [AngularFirebase SSR Videos](https://angularfirebase.com/tag/ssr/)

0 commit comments

Comments
 (0)