Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Angular Universal - essentials
Search
Maciej Treder
October 09, 2019
Programming
250
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
190
WT* is JWT
maciejtreder
0
650
Asynchronous JavaScript - there and back again
maciejtreder
0
1k
Angular Schematics
maciejtreder
1
490
Asynchronous & Synchronous JavaScript. There and back again.
maciejtreder
1
430
E2E SPA tests with protractor
maciejtreder
1
270
Angular Universal - A medicine for the SEO/CDN issues
maciejtreder
1
160
DevIT; Thessaloniki; Progressive Web Apps: Future of web development
maciejtreder
1
140
Other Decks in Programming
See All in Programming
型も通る、synthも通る、それでも危ない 〜AIのCDKの権限とコストを機械で検証する〜 / It Passes Type Checks, It Passes Synth Checks, but It’s Still Risky — Automatically Verifying Permissions and Costs in AI’s CDK —
seike460
PRO
0
110
Embedded SREと共に達成した会員管理システムのAWS移行 - SRE NEXT 2026 ランチスポンサーセッション
niftycorp
PRO
1
2.4k
【やさしく解説 設計編 #1】「ドメイン駆動」と「実装駆動」ってなに? 〜設計の考え方を、たとえ話で学ぼう〜
panda728
PRO
1
120
act1-costs.pdf
sumedhbala
0
210
1B+ /day規模のログを管理する技術
broadleaf
0
130
act2-costs.pdf
sumedhbala
0
110
地域 SRE コミュニティ最前線 - ホンマでっかSRE勉強会
tk3fftk
0
220
壊れたパーサから始める関数型設計と構成的なパーサ #fp_matsuri
raiga0310
2
210
PHPだって関数型したい 〜できること、できないこと〜 / fp-in-php
jsoizo
0
200
過去最大のMCPアップデート! 2026-07-28 RC版の謎に迫る
licux
6
460
アルゴリズムは何を圧縮しているのか ─ Haskell から育った「圧縮代数」というメンタルモデル
naoya
16
3.4k
Generative UI & AI-Assistants for Your Angular Solutions
manfredsteyer
PRO
1
170
Featured
See All Featured
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
35
2.5k
Darren the Foodie - Storyboard
khoart
PRO
3
3.4k
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
1
260
First, design no harm
axbom
PRO
2
1.2k
Measuring Dark Social's Impact On Conversion and Attribution
stephenakadiri
2
230
The SEO Collaboration Effect
kristinabergwall1
1
500
Facilitating Awesome Meetings
lara
57
7k
So, you think you're a good person
axbom
PRO
2
2.1k
Typedesign – Prime Four
hannesfritz
42
3.1k
Fashionably flexible responsive web design (full day workshop)
malarkey
408
67k
Hiding What from Whom? A Critical Review of the History of Programming languages for Music
tomoyanonymous
3
930
What’s in a name? Adding method to the madness
productmarketing
PRO
24
4.1k
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