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
240
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
480
Asynchronous & Synchronous JavaScript. There and back again.
maciejtreder
1
420
E2E SPA tests with protractor
maciejtreder
1
270
Angular Universal - A medicine for the SEO/CDN issues
maciejtreder
1
150
DevIT; Thessaloniki; Progressive Web Apps: Future of web development
maciejtreder
1
140
Other Decks in Programming
See All in Programming
Datadog × OpenTelemetry 入門と実践のあいだ
kn_to_maxpno
1
160
DynamoDBには集計系のクエリがないけどなんとかしたい
musan
1
180
ローカルLLMでどこまでコードが書けるか -拡張版 / How much code can be written on a local LLM Extended
kishida
11
4.3k
Honoでのサプライチェーン侵害対策 〜 3つのライブラリに学ぶ
yusukebe
6
1.3k
LLMによるContent Moderationの本番運用の裏側と品質担保への挑戦
suikabar
3
710
キャリア迷子上等 ─ "ない道"は自分で作ればいい
16bitidol
3
2.1k
AI 時代のソフトウェア設計の学び方
masuda220
PRO
29
13k
脅威をエンジニアリングの糧にして――現場編 / Turning Threats into Engineering Fuel — Field Edition
nrslib
0
290
エンジニアと一緒にテストコードの設計と実装を改善した話
mototakatsu
0
210
PHPで使える日時の表現と、その知り方 #frontend_phpcon_do
o0h
PRO
0
260
Dataformのリポジトリを立ち上げるときにまずやること / dataform-day0-2026
snhryt
0
170
OSもどきOS
arkw
0
570
Featured
See All Featured
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
35
2.5k
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
310
Ruling the World: When Life Gets Gamed
codingconduct
0
260
brightonSEO & MeasureFest 2025 - Christian Goodrich - Winning strategies for Black Friday CRO & PPC
cargoodrich
3
730
Avoiding the “Bad Training, Faster” Trap in the Age of AI
tmiket
0
180
Utilizing Notion as your number one productivity tool
mfonobong
4
320
Believing is Seeing
oripsolob
1
150
Have SEOs Ruined the Internet? - User Awareness of SEO in 2025
akashhashmi
0
370
Making Projects Easy
brettharned
120
6.7k
Information Architects: The Missing Link in Design Systems
soysaucechin
0
970
Art, The Web, and Tiny UX
lynnandtonic
304
22k
Stop Working from a Prison Cell
hatefulcrawdad
274
21k
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