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
680
Asynchronous JavaScript - there and back again
maciejtreder
0
1k
Angular Schematics
maciejtreder
1
510
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
160
Other Decks in Programming
See All in Programming
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
560
Vapor Mode でアクセシビリティは 壊れないか検証した話
fuqda
1
320
선언형 햅틱 라이브러리 진동을 소개합니다
l2hyunwoo
1
280
Enforcing Your Architecture with Unit Tests in the AI Era (Swift Connection X)
steliosf
PRO
1
120
Crash Auto Pipelines
veronikapj
1
340
SODA - FACT BOOK(JP)
sodainc
1
40k
Event is what happens
moro
2
3.2k
AIがコードを書いてくれる時代、我々は何を確かめるのか
yosuke_furukawa
PRO
13
5.6k
クラウドゲーミングのパフォーマンス最適化に挑む / Cloud Gaming Performance Tuning
vivion
0
320
TS 7.0 で baseUrl が使えなくなった今、パスエイリアスをどうするか
bicstone
2
460
우리의 Nitro Modules는 당신의 Turbo Modules보다 아름답다
l2hyunwoo
0
250
Next.js: The final boss of Web Performance
guaca
0
380
Featured
See All Featured
Between Models and Reality
mayunak
4
550
How to make the Groovebox
asonas
2
2.5k
Lightning Talk: Beautiful Slides for Beginners
inesmontani
PRO
2
760
Typedesign – Prime Four
hannesfritz
42
3.2k
Deep Space Network (abreviated)
tonyrice
0
400
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
121
120k
Claude Code どこまでも/ Claude Code Everywhere
nwiizo
68
60k
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
560
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
Have SEOs Ruined the Internet? - User Awareness of SEO in 2025
akashhashmi
0
540
More Than Pixels: Becoming A User Experience Designer
marktimemedia
3
610
The Cost Of JavaScript in 2023
addyosmani
55
10k
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