SlideShare a Scribd company logo
SSR (Server Side Rendering)
Strategies & Technics
About mySelf
• Experienced FE developer, specialised in B2C applications.
• FE Team Lead @ market.co.uk
• Weekends FE developer @ fashbash.co
Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics
Server Side Rendering
Server Side Rendering
What is it?
SSR Will give you the ability
to run and serve your app
from the server
Or in other words…
Pre-Rendering
Angular is SPA
And thats a huge
advantage
But…
There is a cost
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>ServerSideRenderingTalk</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>
Angular
Universal
Fast
Loading
SEO
Crawlable Scrapable
Fast loading
"We found that 53% of mobile site visits
are abandoned if pages take longer than
3 second to load…”
• - - prod (AOT, Minification, tree shacking)
• Lazy Loading
• Soon to by - Ivy
• Opportunity for improve?
• Time between html load & bootstrap
Page load Optimisations
What we want to measure?
Time for first
meaningful paint
Time to interactive
Page Load
Page Load
SSR
Regular

load
0 30 60 90 120
index.html load App bootstrap First view
Social
Scrapeable
Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics
SEO crawlers
Angular server side rendering - Strategies & Technics
They can’t run
JavaScript
And yet…
Universal
can
be a heavy lift
• Unsupported features:
• window object
• document
• localStorage
• nativeElement changed directly
• And More…
Not everything is PINK
Do I really need
this?
“Do new users discover
your app for the first
time through a link”
- Jeff Whelpley, co-founder of Angular Universal
If you need it,
Let’s do it!
Demo…
ng generate universal --client-project *project-name*
CREATE src/main.server.ts (220 bytes)
CREATE src/app/app.server.module.ts (318 bytes)
CREATE src/tsconfig.server.json (219 bytes)
UPDATE package.json (1374 bytes)
UPDATE angular.json (4107 bytes)
UPDATE src/main.ts (430 bytes)
UPDATE src/app/app.module.ts (965 bytes)
renderModuleFactory(AppServerModuleNgFactory, {
document: index,
url: req.url
})
npm install @nguniversal/express-engine --save
npm install @nguniversal/module-map-ngfactory-
loader --save
"server-build": "ng build --prod && ng run server-side-
rendering-talk:server”
"start:server": "./node_modules/.bin/ts-node ./
server.ts"
package.json
enableProdMode();
const app = express();
const distPath = __dirname + '/dist';
app.engine('html', ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [provideModuleMap(LAZY_MODULE_MAP)]
}));
app.set('view engine', 'html');
app.set('views', distPath);
app.get('*.*', express.static(distPath));
app.get('*', (req, res) => {
res.render('index', {req});
});
server.ts
enableProdMode();
const app = express();
const distPath = __dirname + '/dist';
app.engine('html', ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [provideModuleMap(LAZY_MODULE_MAP)]
}));
app.set('view engine', 'html');
app.set('views', distPath);
app.get('*.*', express.static(distPath));
app.get('*', (req, res) => {
res.render('index', {req});
});
server.ts
enableProdMode();
const app = express();
const distPath = __dirname + '/dist';
app.engine('html', ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [provideModuleMap(LAZY_MODULE_MAP)]
}));
app.set('view engine', 'html');
app.set('views', distPath);
app.get('*.*', express.static(distPath));
app.get('*', (req, res) => {
res.render('index', {req});
});
server.ts
enableProdMode();
const app = express();
const distPath = __dirname + '/dist';
app.engine('html', ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [provideModuleMap(LAZY_MODULE_MAP)]
}));
app.set('view engine', 'html');
app.set('views', distPath);
app.get('*.*', express.static(distPath));
app.get('*', (req, res) => {
res.render('index', {req});
});
server.ts
enableProdMode();
const app = express();
const distPath = __dirname + '/dist';
app.engine('html', ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [provideModuleMap(LAZY_MODULE_MAP)]
}));
app.set('view engine', 'html');
app.set('views', distPath);
app.get('*.*', express.static(distPath));
app.get('*', (req, res) => {
res.render('index', {req});
});
server.ts
Control the META
ngOnInit() {
this.paramsSubscription = this.route.data.subscribe((data: any) => {
this.item = data['data'][0];
this.title.setTitle(this.item.description);
this.meta.addTag({
name: 'description',
content: this.item.description
});
});
product-page.component.ts
const twitterTags = [];
twitterTags.push({ name: 'twitter:card', content: 'summary' });
twitterTags.push({ name: 'twitter:site', content: '@EliranEliassy' });
twitterTags.push({ name: 'twitter:title', content: this.item.description });
twitterTags.push({ name: 'twitter:description', content: this.item.description });
twitterTags.push({ name: 'twitter:text:description', content: this.item.description });
twitterTags.push({ name: 'twitter:image', content: this.item.imageUrl });
this.meta.addTags(twitterTags);
product-page.component.ts
const fbTags = [];
fbTags.push({ name: 'og:title', content: this.item.description });
fbTags.push({ name: 'og:image', content: this.item.imageUrl });
fbTags.push({ name: 'og:site_name', content: 'Fake MarketPlace' });
fbTags.push({ name: 'og:description', content: this.item.description });
this.meta.addTags(fbTags);
product-page.component.ts
NOW…Let’s check
the performance
Client Side Rendering
Server Side Rendering
• Time for first meaningful paint: CSR < SSR
• Time to interactive: CSR > SSR
Results
You downloading a lot of text
with your index.html
Angular server side rendering - Strategies & Technics
What can we do?
1. Partially SSR
SsrNoRender Directive
constructor(private viewContainerRef: ViewContainerRef,
private templateRef: TemplateRef<any>,
@Inject(PLATFORM_ID) private platformId) { }
ngOnInit(): void {
if (isPlatformServer(this.platformId)) {
this.viewContainerRef.clear();
} else {
this.viewContainerRef.createEmbeddedView(this.templateRef);
}
}
SsrNoRender Directive
constructor(private viewContainerRef: ViewContainerRef,
private templateRef: TemplateRef<any>,
@Inject(PLATFORM_ID) private platformId) { }
ngOnInit(): void {
if (isPlatformServer(this.platformId)) {
this.viewContainerRef.clear();
} else {
this.viewContainerRef.createEmbeddedView(this.templateRef);
}
}
app.component.html
<app-header></app-header>
<div class="container text-center">
<ng-container *appSsrNoRender>
<router-outlet></router-outlet>
</ng-container>
<div class="loader" *appSsrRender></div>
</div>
2. Angular
Elements
3. Avoid double
XHR requests
Product page resolver
resolve(route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<any> {
const productId = route.params['id'];
return this.productService.getProductById(productId);
}
Product page resolver
constructor(private productService: ProductService,
@Inject(PLATFORM_ID) private platformId,
private transferState: TransferState) { }
Product page resolver
const productId = route.params['id'];
const key = makeStateKey<any>(productId);
if (this.transferState.hasKey(key)) {
const item = this.transferState.get<any>(key, null);
this.transferState.remove(key);
return of(item);
} else {
return this.productService.getProductById(productId)
.pipe(
tap(item => {
if (isPlatformServer(this.platformId)) {
this.transferState.set(key, item);
}
})
);
}
Product page resolver
const productId = route.params['id'];
const key = makeStateKey<any>(productId);
if (this.transferState.hasKey(key)) {
const item = this.transferState.get<any>(key, null);
this.transferState.remove(key);
return of(item);
} else {
return this.productService.getProductById(productId)
.pipe(
tap(item => {
if (isPlatformServer(this.platformId)) {
this.transferState.set(key, item);
}
})
);
}
Product page resolver
const productId = route.params['id'];
const key = makeStateKey<any>(productId);
if (this.transferState.hasKey(key)) {
const item = this.transferState.get<any>(key, null);
this.transferState.remove(key);
return of(item);
} else {
return this.productService.getProductById(productId)
.pipe(
tap(item => {
if (isPlatformServer(this.platformId)) {
this.transferState.set(key, item);
}
})
);
}
4. Server-Cache
your response
const cache = new Map();
export function ngExpressEngine(setupOptions) {
return function (filePath, options, callback) {
if (!cache.has(filePath)) {
const content = fs.readFileSync(filePath).toString();
cache.set(filePath, content);
}
renderModuleFactory(setupOptions.bootstrap[0], {
document: cache.get(filePath),
url: options.req.url
})
.then(string => {
callback(null, string);
});
};
}
server.ts
const cache = new Map();
export function ngExpressEngine(setupOptions) {
return function (filePath, options, callback) {
if (!cache.has(filePath)) {
const content = fs.readFileSync(filePath).toString();
cache.set(filePath, content);
}
renderModuleFactory(setupOptions.bootstrap[0], {
document: cache.get(filePath),
url: options.req.url
})
.then(string => {
callback(null, string);
});
};
}
server.ts
Did I said
that it can
be a
heavy lift?
Angular server side rendering - Strategies & Technics
Deployment
Run it on the cloud
• Super FAST response time
• Cheaper than servers instances
• Easy to deploy
Thank You
@EliranEliassy eliran.eliassy@gmail.comeliraneliassy

More Related Content

PDF
Server-Side Rendering (SSR) with Angular Universal
PPTX
Angular 9
PDF
Angular components
PDF
Intro To React Native
PPTX
Express js
PPTX
Angular modules in depth
PPTX
React Native
PDF
Angular Routing Guard
Server-Side Rendering (SSR) with Angular Universal
Angular 9
Angular components
Intro To React Native
Express js
Angular modules in depth
React Native
Angular Routing Guard

What's hot (20)

PPTX
Angular 5 presentation for beginners
PPTX
Express JS Middleware Tutorial
PDF
Angular and The Case for RxJS
PDF
Building blocks of Angular
PDF
NodeJS for Beginner
PDF
Spring Boot & Actuators
PDF
Spring boot introduction
PPTX
iOS Coding Best Practices
PPT
Angular 8
PPTX
MVVM ( Model View ViewModel )
PDF
Angular Dependency Injection
PDF
Asp.net state management
PDF
Angular material
PPTX
Maven
PDF
Angular - Chapter 9 - Authentication and Authorization
PDF
Angular directives and pipes
PPTX
Introducing type script
PPT
Spring Framework
PPTX
Angular interview questions
PDF
Introduction to Swift programming language.
Angular 5 presentation for beginners
Express JS Middleware Tutorial
Angular and The Case for RxJS
Building blocks of Angular
NodeJS for Beginner
Spring Boot & Actuators
Spring boot introduction
iOS Coding Best Practices
Angular 8
MVVM ( Model View ViewModel )
Angular Dependency Injection
Asp.net state management
Angular material
Maven
Angular - Chapter 9 - Authentication and Authorization
Angular directives and pipes
Introducing type script
Spring Framework
Angular interview questions
Introduction to Swift programming language.
Ad

Similar to Angular server side rendering - Strategies & Technics (20)

PDF
Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...
PDF
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
KEY
Android app development basics
PPT
Building Single Page Application (SPA) with Symfony2 and AngularJS
PDF
Node.js server-side rendering
PDF
Introducing Rendr: Run your Backbone.js apps on the client and server
PDF
Fragments: Why, How, What For?
PPTX
Mobile App Development: Primi passi con NativeScript e Angular 2
PDF
Heroku pop-behind-the-sense
PPTX
Building Web Apps with Express
PDF
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
PPTX
AngularJS training - Day 1 - Basics: Why, What and basic features of AngularJS
PPTX
Let's react - Meetup
PPT
GHC
PPTX
Building a dashboard using AngularJS
PDF
A gently introduction to AngularJS
PDF
Universal JavaScript Web Applications with React - Luciano Mammino - Codemoti...
PDF
Universal JS Web Applications with React - Luciano Mammino - Codemotion Rome ...
PPTX
Rits Brown Bag - Salesforce Lightning
PDF
A Story about AngularJS modularization development
Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
Android app development basics
Building Single Page Application (SPA) with Symfony2 and AngularJS
Node.js server-side rendering
Introducing Rendr: Run your Backbone.js apps on the client and server
Fragments: Why, How, What For?
Mobile App Development: Primi passi con NativeScript e Angular 2
Heroku pop-behind-the-sense
Building Web Apps with Express
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
AngularJS training - Day 1 - Basics: Why, What and basic features of AngularJS
Let's react - Meetup
GHC
Building a dashboard using AngularJS
A gently introduction to AngularJS
Universal JavaScript Web Applications with React - Luciano Mammino - Codemoti...
Universal JS Web Applications with React - Luciano Mammino - Codemotion Rome ...
Rits Brown Bag - Salesforce Lightning
A Story about AngularJS modularization development
Ad

More from Eliran Eliassy (10)

PDF
Angular - Improve Runtime performance 2019
PDF
Between JS and AI
PDF
Ngrx meta reducers
PDF
Angular CDK
PDF
Angular - injection tokens & Custom libraries
PDF
Runtime performance
PDF
Intro to HTML and CSS basics
PDF
Angular performance improvments
PDF
Angular genericforms2
PPTX
Generic forms
Angular - Improve Runtime performance 2019
Between JS and AI
Ngrx meta reducers
Angular CDK
Angular - injection tokens & Custom libraries
Runtime performance
Intro to HTML and CSS basics
Angular performance improvments
Angular genericforms2
Generic forms

Recently uploaded (20)

PDF
cuic standard and advanced reporting.pdf
PPTX
MYSQL Presentation for SQL database connectivity
PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
Empathic Computing: Creating Shared Understanding
PPTX
A Presentation on Artificial Intelligence
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PPTX
Spectroscopy.pptx food analysis technology
PDF
Encapsulation_ Review paper, used for researhc scholars
PDF
Encapsulation theory and applications.pdf
PPTX
sap open course for s4hana steps from ECC to s4
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PDF
Spectral efficient network and resource selection model in 5G networks
PPTX
Cloud computing and distributed systems.
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PPT
Teaching material agriculture food technology
PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PDF
Chapter 3 Spatial Domain Image Processing.pdf
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
cuic standard and advanced reporting.pdf
MYSQL Presentation for SQL database connectivity
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Empathic Computing: Creating Shared Understanding
A Presentation on Artificial Intelligence
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Spectroscopy.pptx food analysis technology
Encapsulation_ Review paper, used for researhc scholars
Encapsulation theory and applications.pdf
sap open course for s4hana steps from ECC to s4
Advanced methodologies resolving dimensionality complications for autism neur...
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Spectral efficient network and resource selection model in 5G networks
Cloud computing and distributed systems.
The Rise and Fall of 3GPP – Time for a Sabbatical?
Teaching material agriculture food technology
Building Integrated photovoltaic BIPV_UPV.pdf
Chapter 3 Spatial Domain Image Processing.pdf
Agricultural_Statistics_at_a_Glance_2022_0.pdf

Angular server side rendering - Strategies & Technics

  • 1. SSR (Server Side Rendering) Strategies & Technics
  • 2. About mySelf • Experienced FE developer, specialised in B2C applications. • FE Team Lead @ market.co.uk • Weekends FE developer @ fashbash.co
  • 8. SSR Will give you the ability to run and serve your app from the server
  • 9. Or in other words… Pre-Rendering
  • 11. And thats a huge advantage But… There is a cost
  • 12. <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>ServerSideRenderingTalk</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> </head> <body> <app-root></app-root> </body> </html>
  • 15. "We found that 53% of mobile site visits are abandoned if pages take longer than 3 second to load…”
  • 16. • - - prod (AOT, Minification, tree shacking) • Lazy Loading • Soon to by - Ivy • Opportunity for improve? • Time between html load & bootstrap Page load Optimisations
  • 17. What we want to measure? Time for first meaningful paint Time to interactive
  • 19. Page Load SSR Regular
 load 0 30 60 90 120 index.html load App bootstrap First view
  • 28. • Unsupported features: • window object • document • localStorage • nativeElement changed directly • And More… Not everything is PINK
  • 29. Do I really need this?
  • 30. “Do new users discover your app for the first time through a link” - Jeff Whelpley, co-founder of Angular Universal
  • 31. If you need it, Let’s do it! Demo…
  • 32. ng generate universal --client-project *project-name* CREATE src/main.server.ts (220 bytes) CREATE src/app/app.server.module.ts (318 bytes) CREATE src/tsconfig.server.json (219 bytes) UPDATE package.json (1374 bytes) UPDATE angular.json (4107 bytes) UPDATE src/main.ts (430 bytes) UPDATE src/app/app.module.ts (965 bytes)
  • 34. npm install @nguniversal/express-engine --save npm install @nguniversal/module-map-ngfactory- loader --save
  • 35. "server-build": "ng build --prod && ng run server-side- rendering-talk:server” "start:server": "./node_modules/.bin/ts-node ./ server.ts" package.json
  • 36. enableProdMode(); const app = express(); const distPath = __dirname + '/dist'; app.engine('html', ngExpressEngine({ bootstrap: AppServerModuleNgFactory, providers: [provideModuleMap(LAZY_MODULE_MAP)] })); app.set('view engine', 'html'); app.set('views', distPath); app.get('*.*', express.static(distPath)); app.get('*', (req, res) => { res.render('index', {req}); }); server.ts
  • 37. enableProdMode(); const app = express(); const distPath = __dirname + '/dist'; app.engine('html', ngExpressEngine({ bootstrap: AppServerModuleNgFactory, providers: [provideModuleMap(LAZY_MODULE_MAP)] })); app.set('view engine', 'html'); app.set('views', distPath); app.get('*.*', express.static(distPath)); app.get('*', (req, res) => { res.render('index', {req}); }); server.ts
  • 38. enableProdMode(); const app = express(); const distPath = __dirname + '/dist'; app.engine('html', ngExpressEngine({ bootstrap: AppServerModuleNgFactory, providers: [provideModuleMap(LAZY_MODULE_MAP)] })); app.set('view engine', 'html'); app.set('views', distPath); app.get('*.*', express.static(distPath)); app.get('*', (req, res) => { res.render('index', {req}); }); server.ts
  • 39. enableProdMode(); const app = express(); const distPath = __dirname + '/dist'; app.engine('html', ngExpressEngine({ bootstrap: AppServerModuleNgFactory, providers: [provideModuleMap(LAZY_MODULE_MAP)] })); app.set('view engine', 'html'); app.set('views', distPath); app.get('*.*', express.static(distPath)); app.get('*', (req, res) => { res.render('index', {req}); }); server.ts
  • 40. enableProdMode(); const app = express(); const distPath = __dirname + '/dist'; app.engine('html', ngExpressEngine({ bootstrap: AppServerModuleNgFactory, providers: [provideModuleMap(LAZY_MODULE_MAP)] })); app.set('view engine', 'html'); app.set('views', distPath); app.get('*.*', express.static(distPath)); app.get('*', (req, res) => { res.render('index', {req}); }); server.ts
  • 42. ngOnInit() { this.paramsSubscription = this.route.data.subscribe((data: any) => { this.item = data['data'][0]; this.title.setTitle(this.item.description); this.meta.addTag({ name: 'description', content: this.item.description }); }); product-page.component.ts
  • 43. const twitterTags = []; twitterTags.push({ name: 'twitter:card', content: 'summary' }); twitterTags.push({ name: 'twitter:site', content: '@EliranEliassy' }); twitterTags.push({ name: 'twitter:title', content: this.item.description }); twitterTags.push({ name: 'twitter:description', content: this.item.description }); twitterTags.push({ name: 'twitter:text:description', content: this.item.description }); twitterTags.push({ name: 'twitter:image', content: this.item.imageUrl }); this.meta.addTags(twitterTags); product-page.component.ts
  • 44. const fbTags = []; fbTags.push({ name: 'og:title', content: this.item.description }); fbTags.push({ name: 'og:image', content: this.item.imageUrl }); fbTags.push({ name: 'og:site_name', content: 'Fake MarketPlace' }); fbTags.push({ name: 'og:description', content: this.item.description }); this.meta.addTags(fbTags); product-page.component.ts
  • 48. • Time for first meaningful paint: CSR < SSR • Time to interactive: CSR > SSR Results You downloading a lot of text with your index.html
  • 50. What can we do?
  • 52. SsrNoRender Directive constructor(private viewContainerRef: ViewContainerRef, private templateRef: TemplateRef<any>, @Inject(PLATFORM_ID) private platformId) { } ngOnInit(): void { if (isPlatformServer(this.platformId)) { this.viewContainerRef.clear(); } else { this.viewContainerRef.createEmbeddedView(this.templateRef); } }
  • 53. SsrNoRender Directive constructor(private viewContainerRef: ViewContainerRef, private templateRef: TemplateRef<any>, @Inject(PLATFORM_ID) private platformId) { } ngOnInit(): void { if (isPlatformServer(this.platformId)) { this.viewContainerRef.clear(); } else { this.viewContainerRef.createEmbeddedView(this.templateRef); } }
  • 54. app.component.html <app-header></app-header> <div class="container text-center"> <ng-container *appSsrNoRender> <router-outlet></router-outlet> </ng-container> <div class="loader" *appSsrRender></div> </div>
  • 57. Product page resolver resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> { const productId = route.params['id']; return this.productService.getProductById(productId); }
  • 58. Product page resolver constructor(private productService: ProductService, @Inject(PLATFORM_ID) private platformId, private transferState: TransferState) { }
  • 59. Product page resolver const productId = route.params['id']; const key = makeStateKey<any>(productId); if (this.transferState.hasKey(key)) { const item = this.transferState.get<any>(key, null); this.transferState.remove(key); return of(item); } else { return this.productService.getProductById(productId) .pipe( tap(item => { if (isPlatformServer(this.platformId)) { this.transferState.set(key, item); } }) ); }
  • 60. Product page resolver const productId = route.params['id']; const key = makeStateKey<any>(productId); if (this.transferState.hasKey(key)) { const item = this.transferState.get<any>(key, null); this.transferState.remove(key); return of(item); } else { return this.productService.getProductById(productId) .pipe( tap(item => { if (isPlatformServer(this.platformId)) { this.transferState.set(key, item); } }) ); }
  • 61. Product page resolver const productId = route.params['id']; const key = makeStateKey<any>(productId); if (this.transferState.hasKey(key)) { const item = this.transferState.get<any>(key, null); this.transferState.remove(key); return of(item); } else { return this.productService.getProductById(productId) .pipe( tap(item => { if (isPlatformServer(this.platformId)) { this.transferState.set(key, item); } }) ); }
  • 63. const cache = new Map(); export function ngExpressEngine(setupOptions) { return function (filePath, options, callback) { if (!cache.has(filePath)) { const content = fs.readFileSync(filePath).toString(); cache.set(filePath, content); } renderModuleFactory(setupOptions.bootstrap[0], { document: cache.get(filePath), url: options.req.url }) .then(string => { callback(null, string); }); }; } server.ts
  • 64. const cache = new Map(); export function ngExpressEngine(setupOptions) { return function (filePath, options, callback) { if (!cache.has(filePath)) { const content = fs.readFileSync(filePath).toString(); cache.set(filePath, content); } renderModuleFactory(setupOptions.bootstrap[0], { document: cache.get(filePath), url: options.req.url }) .then(string => { callback(null, string); }); }; } server.ts
  • 65. Did I said that it can be a heavy lift?
  • 68. Run it on the cloud • Super FAST response time • Cheaper than servers instances • Easy to deploy