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
260
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
660
Asynchronous JavaScript - there and back again
maciejtreder
0
1k
Angular Schematics
maciejtreder
1
490
Asynchronous & Synchronous JavaScript. There and back again.
maciejtreder
1
440
E2E SPA tests with protractor
maciejtreder
1
280
Angular Universal - A medicine for the SEO/CDN issues
maciejtreder
1
160
DevIT; Thessaloniki; Progressive Web Apps: Future of web development
maciejtreder
1
150
Other Decks in Programming
See All in Programming
選挙速報を多くのユーザーへ 届ける Live Activities 設計
hamayokokuririn
0
180
すこし踏み込む CancellationToken
htkym
2
1.3k
Webの地図
yosuke_furukawa
PRO
6
4.8k
SREの越境 / SRE Collaboration
y0hgi
2
290
Vue Fes Japan 2026 タイムテーブル徹底解説
448jp
1
550
AHC070解法紹介
eijirou
0
140
FreeBSDでZabbixを動かす
kenkino
0
330
巨大モノリシックアプリ モダン化大作戦
ktcryomm
1
1.1k
Snowflakeで業務アプリを作ろう。 Snowflakeのアプリ機能解説&実践ガイド
ayumu_yamaguchi
2
310
モバイル交通系ICへのチャージ実例から考える、クロスプラットフォーム開発におけるiOS実機テスト設計とCI運用
yusuga
1
540
テストを司るデーモンに会いに行く 〜隔離した仮想マシンでテストを通すまで〜
h1d3mun3
1
580
JPUG勉強会 OSSデータベースの内部構造を理解しよう(第2回)
oga5
0
280
Featured
See All Featured
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
4.6k
[RailsConf 2023] Rails as a piece of cake
palkan
59
7k
Building a Scalable Design System with Sketch
lauravandoore
464
34k
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.9k
Navigating Weather and Climate Data
rabernat
0
540
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
2
2.2k
The Illustrated Guide to Node.js - THAT Conference 2024
reverentgeek
1
540
Exploring the relationship between traditional SERPs and Gen AI search
raygrieselhuber
PRO
3
4.3k
A designer walks into a library…
pauljervisheath
211
25k
Introduction to Domain-Driven Design and Collaborative software design
baasie
1
1k
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
2
290
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
550
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