Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Angular Universal - essentials

Angular Universal - essentials

Maciej Treder

October 09, 2019
Tweet

More Decks by Maciej Treder

Other Decks in Programming

Transcript

  1. @maciejtreder

    View Slide

  2. Outline
    • SPA pitfall
    • Server-side rendering
    • API optimization
    SPA pitfall SSR APIs

    View Slide

  3. SPA problem

    RewriteEngine On
    RewriteBase /
    RewriteRule ^index\.html$ - [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . index.html [L]

    .htaccess
    SPA pitfall

    View Slide

  4. SPA Problem
    GET /
    GET /anotherPage
    index.html
    GET /subpage
    GET
    /contact
    GET /home
    SPA pitfall

    View Slide

  5. SPA Problem
    GET / GET /anotherPage
    SPA pitfall

    View Slide

  6. SPA Problem
    SPA pitfall

    View Slide

  7. Server Side Rendering
    SSR

    View Slide

  8. Server Side Rendering
    GET /
    GET /anotherPage
    SSR

    View Slide

  9. Is it worth?
    curl localhost:8080


    SomeProject



    <br/>/*# sourceMappingURL=data:application/<br/>json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsImZpbGUiOiJzcmMvYXBwL2FwcC5jb21wb25lbnQuY3NzIn0= */</<br/>style></head><br/><body><br/><script type="text/javascript" src="runtime.26209474bfa8dc87a77c.js"></script><script type="text/javascript" src="es2015-<br/>polyfills.c5dd28b362270c767b34.js" nomodule=""></script><script type="text/javascript" src="polyfills.8bbb231b43165d65d357.js"></<br/>script><script type="text/javascript" src="main.8a9128130a3a38dd7ee5.js"></script><br/><app-root _nghost-sc0="" ng-version="7.2.9"><div _ngcontent-sc0="" style="text-align:center"><h1 _ngcontent-sc0=""> Welcome<br/>to someProject! </h1><img _ngcontent-sc0="" alt="Angular Logo" src="data:image/<br/>svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERD<br/>AwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJ<br/>GIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1<br/>Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogI<br/>Dwvc3ZnPg==" width="300"></div><h2 _ngcontent-sc0="">Here are some links to help you start: </h2><ul _ngcontent-sc0=""><li<br/>_ngcontent-sc0=""><h2 _ngcontent-sc0=""><a _ngcontent-sc0="" href="https://angular.io/tutorial" rel="noopener"<br/>target="_blank">Tour of Heroes</a></h2></li><li _ngcontent-sc0=""><h2 _ngcontent-sc0=""><a _ngcontent-sc0="" href="https://<br/>angular.io/cli" rel="noopener" target="_blank">CLI Documentation</a></h2></li><li _ngcontent-sc0=""><h2 _ngcontent-sc0=""><a<br/>_ngcontent-sc0="" href="https://blog.angular.io/" rel="noopener" target="_blank">Angular blog</a></h2></li></ul></app-root><br/>SSR<br/>

    View Slide

  10. —prod vs. universal
    Load HTML Bootstrap
    Load HTML Bootstrap
    SSR
    NO SSR First meaningful paint
    First meaningful paint
    SSR

    View Slide

  11. How to start?
    SSR
    ng add @nguniversal/express-engine ng add @ng-toolkit/universal

    View Slide

  12. 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

    View Slide

  13. 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

    View Slide

  14. 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

    View Slide

  15. 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

    View Slide

  16. Under the hood
    app.set('view engine', 'html');
    app.set('views', './dist/browser');
    app.get('*.*', express.static('./dist/browser', {
    maxAge: '1y'
    }));
    server.ts
    SSR

    View Slide

  17. 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

    View Slide

  18. API optimization
    APIs

    View Slide

  19. DRY(c)
    Don’t repeat your calls
    export class AppComponent implements OnInit {
    public post: Observable;
    constructor(private httpClient: HttpClient) {}
    public ngOnInit(): void {
    this.post = this.httpClient.get('https://jsonplaceholder.typicode.com/posts/1');
    }
    }
    APIs

    View Slide

  20. 2
    1
    3
    4
    5 6
    external.api.com
    APIs

    View Slide

  21. external.api.com
    my-website.com
    1
    2
    3
    APIs

    View Slide

  22. 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

    View Slide

  23. TransferState
    • ServerTransferStateModule (@angular/platform-server)
    • BrowserTransferStateModule (@angular/platform-browser)
    • get(key, fallbackValue)
    • set(key, value)
    • has(key)
    • remove(key)
    APIs

    View Slide

  24. 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

    View Slide

  25. HTTP_INTERCEPTOR
    @Injectable()
    export class ServerStateInterceptor implements HttpInterceptor {
    constructor(private _transferState: TransferState) {}
    intercept(req: HttpRequest, next: HttpHandler): Observable> {
    return next.handle(req).pipe(tap(event => {
    if (event instanceof HttpResponse) {
    this._transferState.set(makeStateKey(req.url), event.body);
    }
    }));
    }
    }
    APIs

    View Slide

  26. HTTP_INTERCEPTOR
    @Injectable()
    export class BrowserStateInterceptor implements HttpInterceptor {
    constructor(private _transferState: TransferState) { }
    intercept(req: HttpRequest, next: HttpHandler): Observable> {
    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

    View Slide

  27. 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

    View Slide

  28. https://www.twilio.com/blog/faster-javascript-web-apps-angular-
    universal-transferstate-api-watchdog
    APIs

    View Slide

  29. APIs

    View Slide

  30. Case Study
    /protected
    /login
    /auth/login Authy ID
    uuid
    uuid
    /auth/status
    uuid

    View Slide

  31. Remembered User
    /protected
    /login
    Authy ID
    uuid
    /auth/status
    uuid
    Authy ID from cookie
    /auth/login
    uuid
    + uuid

    View Slide

  32. https://www.twilio.com/blog/expedited-two-factor-authentication-
    angular-twilio-authy
    APIs

    View Slide

  33. APIs

    View Slide

  34. @maciejtreder

    View Slide