Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Angular Universal - essentials
Search
Maciej Treder
October 09, 2019
Programming
270
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Angular Universal - essentials
Maciej Treder
October 09, 2019
More Decks by Maciej Treder
See All by Maciej Treder
WT* is JWT
maciejtreder
0
200
WT* is JWT
maciejtreder
0
670
Asynchronous JavaScript - there and back again
maciejtreder
0
1k
Angular Schematics
maciejtreder
1
500
Asynchronous & Synchronous JavaScript. There and back again.
maciejtreder
1
450
E2E SPA tests with protractor
maciejtreder
1
290
Angular Universal - A medicine for the SEO/CDN issues
maciejtreder
1
170
DevIT; Thessaloniki; Progressive Web Apps: Future of web development
maciejtreder
1
150
Other Decks in Programming
See All in Programming
Railsのためのパスキー実践入門 〜マルチテナントSaaSでの意思決定〜
utakaha
3
790
Findy - エンジニア向け会社紹介/Findy Company Deck
findyinc
6
400k
[ハンズオン]AIへの指示だけで「五目並べ」を作ってみよう
satoshi256kbyte
1
380
UPDATE をやめる — EF Core でマスタをバージョン管理する
panda728
PRO
0
1.2k
Workers Cache を知る
syumai
0
370
KiroのSpecで「五目並べ」を作ってみる
satoshi256kbyte
1
380
[Dev Group Paris Octobre 26] Agentforce, SIforce, Claudeforce, Slackbot… on s'y retrouve ?
nabondance
0
110
なぜCTOを降りてFDEを選んだのか?〜なぜプロダクト企業がFDEで顧客の現場に踏み込むのか〜
gonta
1
220
コーディング自動化の次へ。人の介入をゼロにするファクトリーとサポートAIによる実践
timakin
5
4k
golang-bob.pdf
iwatsukayura
0
130
半永久的に提供し続けられるプライベートクラウドを目指して ― 利用者の認知負荷を抑えるAPI抽象化とハードウェア世代交代の基盤設計
tomokon
0
510
AWS CDKのカスタムリソースでContinuum(旧Security Agent)を実装した話 | Implementing Continuum (formerly Security Agent) via AWS CDK Custom Resource
akihisaikeda
2
150
Featured
See All Featured
Producing Creativity
orderedlist
PRO
348
41k
How Software Deployment tools have changed in the past 20 years
geshan
2
35k
Why Mistakes Are the Best Teachers: Turning Failure into a Pathway for Growth
auna
0
330
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
610
A Modern Web Designer's Workflow
chriscoyier
699
190k
The Invisible Side of Design
smashingmag
301
52k
Kristin Tynski - Automating Marketing Tasks With AI
techseoconnect
PRO
0
550
Building AI with AI
inesmontani
PRO
1
1.3k
How to Think Like a Performance Engineer
csswizardry
28
2.9k
YesSQL, Process and Tooling at Scale
rocio
174
15k
Amusing Abliteration
ianozsvald
1
350
Done Done
chrislema
187
17k
Transcript
@maciejtreder
Outline • SPA pitfall • Server-side rendering • API optimization
SPA pitfall SSR APIs
SPA problem <IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteRule ^index\.html$
- [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.html [L] </IfModule> .htaccess SPA pitfall
SPA Problem GET / GET /anotherPage index.html GET /subpage GET
/contact GET /home SPA pitfall
SPA Problem GET / GET /anotherPage SPA pitfall
SPA Problem SPA pitfall
Server Side Rendering SSR
Server Side Rendering GET / GET /anotherPage SSR
Is it worth? curl localhost:8080 <!DOCTYPE html><html lang="en"><head> <meta charset="utf-8">
<title>SomeProject</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> <link rel="stylesheet" href="styles.3ff695c00d717f2d2a11.css"><style ng-transition="app-root"> /*# sourceMappingURL=data:application/ json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsImZpbGUiOiJzcmMvYXBwL2FwcC5jb21wb25lbnQuY3NzIn0= */</ style></head> <body> <script type="text/javascript" src="runtime.26209474bfa8dc87a77c.js"></script><script type="text/javascript" src="es2015- polyfills.c5dd28b362270c767b34.js" nomodule=""></script><script type="text/javascript" src="polyfills.8bbb231b43165d65d357.js"></ script><script type="text/javascript" src="main.8a9128130a3a38dd7ee5.js"></script> <app-root _nghost-sc0="" ng-version="7.2.9"><div _ngcontent-sc0="" style="text-align:center"><h1 _ngcontent-sc0=""> Welcome to someProject! </h1><img _ngcontent-sc0="" alt="Angular Logo" src="data:image/ svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERD AwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJ GIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1 Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogI Dwvc3ZnPg==" width="300"></div><h2 _ngcontent-sc0="">Here are some links to help you start: </h2><ul _ngcontent-sc0=""><li _ngcontent-sc0=""><h2 _ngcontent-sc0=""><a _ngcontent-sc0="" href="https://angular.io/tutorial" rel="noopener" target="_blank">Tour of Heroes</a></h2></li><li _ngcontent-sc0=""><h2 _ngcontent-sc0=""><a _ngcontent-sc0="" href="https:// angular.io/cli" rel="noopener" target="_blank">CLI Documentation</a></h2></li><li _ngcontent-sc0=""><h2 _ngcontent-sc0=""><a _ngcontent-sc0="" href="https://blog.angular.io/" rel="noopener" target="_blank">Angular blog</a></h2></li></ul></app-root> SSR
—prod vs. universal Load HTML Bootstrap Load HTML Bootstrap SSR
NO SSR First meaningful paint First meaningful paint SSR
How to start? SSR ng add @nguniversal/express-engine ng add @ng-toolkit/universal
Adjust your modules app.module.ts app.server.module.ts @NgModule({ bootstrap: [AppComponent], imports: [
BrowserModule.withServerTransition({appId: //other imports ], }) export class AppModule {} import {NgModule} from '@angular/core'; import {ServerModule} from '@angular/platform-server'; import {ModuleMapLoaderModule} from ‘@nguniversal/module-map-ngfactory-loader'; import {AppModule} from './app.module'; import {AppComponent} from './app.component'; @NgModule({ imports: [ AppModule, ServerModule, ModuleMapLoaderModule ], bootstrap: [AppComponent], }) export class AppServerModule {} SSR
Adjust your modules Official guide app.module.ts app.server.module.ts @NgModule({ declarations: [AppComponent],
imports: [ //common imports ] }) export class AppModule {} import {NgModule} from '@angular/core'; import {ServerModule} from '@angular/platform-server'; import {ModuleMapLoaderModule} from ‘@nguniversal/module-map-ngfactory-loader'; import {AppModule} from './app.module'; import {AppComponent} from './app.component'; @NgModule({ imports: [ AppModule, ServerModule, ModuleMapLoaderModule, //server specific imports ], bootstrap: [AppComponent], }) export class AppServerModule {} app.browser.module.ts @NgModule({ bootstrap: [AppComponent], imports: [ AppModule, BrowserModule.withServerTransition({appId: ' //browser specific imports ] }) export class AppModule {} //browser specific imports //server specific imports SSR
Under the hood export const app = express(); app.use(compression()); app.use(cors());
app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); const {AppServerModuleNgFactory, LAZY_MODULE_MAP} = require('./dist/server/main'); app.engine('html', ngExpressEngine({ bootstrap: AppServerModuleNgFactory, providers: [ provideModuleMap(LAZY_MODULE_MAP) ] })); server.ts SSR
Under the hood app.get('/*', (req, res) => { res.render('index', {req,
res}, (err, html) => { if (html) { res.send(html); } else { console.error(err); res.send(err); } }); }); server.ts SSR
Under the hood app.set('view engine', 'html'); app.set('views', './dist/browser'); app.get('*.*', express.static('./dist/browser',
{ maxAge: '1y' })); server.ts SSR
And let’s go! • npm run build:ssr • npm run
serve:ssr Date: 2018-11-21T13:04:33.302Z Hash: 1a82cb687d2e22b5d12b Time: 10752ms chunk {0} runtime.ec2944dd8b20ec099bf3.js (runtime) 1.41 kB [entry] [rendered] chunk {1} main.09093ffa4ad7f66bc6ff.js (main) 169 kB [initial] [rendered] chunk {2} polyfills.c6871e56cb80756a5498.js (polyfills) 37.5 kB [initial] [rendered] chunk {3} styles.3bb2a9d4949b7dc120a9.css (styles) 0 bytes [initial] [rendered] >
[email protected]
server /Users/mtreder/myApp > node local.js Listening on: http://localhost:8080 SSR
API optimization APIs
DRY(c) Don’t repeat your calls export class AppComponent implements OnInit
{ public post: Observable<any>; constructor(private httpClient: HttpClient) {} public ngOnInit(): void { this.post = this.httpClient.get('https://jsonplaceholder.typicode.com/posts/1'); } } APIs
2 1 3 4 5 6 external.api.com APIs
external.api.com my-website.com 1 2 3 APIs
HttpCacheModule npm install @nguniversal/common import { NgtUniversalModule } from '@ng-toolkit/universal';
import { CommonModule } from '@angular/common'; import { HttpClientModule } from '@angular/common/http'; import { TransferHttpCacheModule } from '@nguniversal/common'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports:[ CommonModule, NgtUniversalModule, TransferHttpCacheModule, HttpClientModule ] }) export class AppModule { } APIs
TransferState • ServerTransferStateModule (@angular/platform-server) • BrowserTransferStateModule (@angular/platform-browser) • get(key, fallbackValue)
• set(key, value) • has(key) • remove(key) APIs
HTTP_INTERCEPTOR • Provided in the AppModule • Every http request
made with HttpClient goes threw it • Used to transform request or response ie: • Adding authentication headers APIs
HTTP_INTERCEPTOR @Injectable() export class ServerStateInterceptor implements HttpInterceptor { constructor(private _transferState:
TransferState) {} intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { return next.handle(req).pipe(tap(event => { if (event instanceof HttpResponse) { this._transferState.set(makeStateKey(req.url), event.body); } })); } } APIs
HTTP_INTERCEPTOR @Injectable() export class BrowserStateInterceptor implements HttpInterceptor { constructor(private _transferState:
TransferState) { } intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { if (req.method !== 'GET') { return next.handle(req); } const storedResponse: string = this._transferState.get(makeStateKey(req.url), null); if (storedResponse) { const response = new HttpResponse({ body: storedResponse, status: 200 }); this._transferState.remove(makeStateKey(req.url)); return of(response); } return next.handle(req); } } APIs
HTTP_INTERCEPTOR import {HTTP_INTERCEPTORS } from '@angular/common/http'; providers: [ { provide:
HTTP_INTERCEPTORS, useClass: BrowserStateInterceptor, multi: true, } ] import {HTTP_INTERCEPTORS } from '@angular/common/http'; providers: [ { provide: HTTP_INTERCEPTORS, useClass: ServerStateInterceptor, multi: true, } ] APIs
https://www.twilio.com/blog/faster-javascript-web-apps-angular- universal-transferstate-api-watchdog APIs
APIs
Case Study /protected /login /auth/login Authy ID uuid uuid /auth/status
uuid
Remembered User /protected /login Authy ID uuid /auth/status uuid Authy
ID from cookie /auth/login uuid + uuid
https://www.twilio.com/blog/expedited-two-factor-authentication- angular-twilio-authy APIs
APIs
@maciejtreder