Transferring state from the server to the client

Since we are using HttpClient to get posts from the server, one potential problem that could occur in server-rendered, client-side applications is that the API is called twice, once on the server and once in the client. You can check that in the Network tab of the developer tools. Observe the screenshots in the Network tab; you will see that, at 630 ms and 747 ms, the content was available on the page, and then all of a sudden at 1.21 s and 2.03 s, the content was removed and comes back at 2.35 s onward. This happens because, at 630 s, we got data in HTML format from the server, and at 1.21 s, Angular became bootstrapped and called the /posts API:

To overcome this issue, we use TransferStateModule, which allows us to cache any data when the code runs on the server and use that cached data instead of calling the API. First, let's configure TransferStateModule. To configure TransferStateModule, we need to include two different modules, one in AppServerModule and one in AppModule:

  1. Let's add BrowserTransferStateModule to our AppModule, as follows:
      import { BrowserModule, BrowserTransferStateModule } from  
'@angular/platform-browser';
...

@NgModule({
...
imports: [
BrowserModule.withServerTransition({ appId: 'serverApp' }),
BrowserTransferStateModule,
...
],
...
})
export class AppModule { }
  1. In AppServerModule, include ServerTransferStateModuleas follows:
      import { ServerModule, ServerTransferStateModule } from 
'@angular/platform-server';

...
@NgModule({
imports: [
AppModule,
ServerModule,
ServerTransferStateModule,
ModuleMapLoaderModule,
],
bootstrap: [AppComponent],
})
export class AppServerModule {}

Now that the appropriate modules for TransferState have been included in AppModule and AppServerModule, we can create an interceptor that intercepts our API call and use the TransferState service to cache data when the application runs on the server. 

  1. Manually create the transfer-state.interceptor.ts file in the app folder and use the snippet provided by Angular snippets, a-http-interceptor, which should create the following snippet:
      import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpEvent, HttpHandler, HttpRequest } from
'@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({providedIn: 'root'})
export class HeaderInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler):
Observable<HttpEvent<any>> {
return next.handle(req);
}
}
  1. Update the name of the class from HeaderInterceptor to TransferStateInterceptor and add this service to our AppModule:
      ...
import
{ HTTP_INTERCEPTORS } from '@angular/common/http';
import { TransferStateInterceptor } from '
./transfer-
state.interceptor';


@NgModule({
...
providers: [{
provide: HTTP_INTERCEPTORS,
useClass: TransferStateInterceptor,
multi: true,
}
],
...
})
export class AppModule { }
  1. Now, we need to identify the platform where our Angular application is running. We can check whether it is running in our server by using isPlatformServer. This needs PLATFORM_ID to work, which can be injected. When the request returns the response, we'll use transferState to set the StateKey, which we can create using makeStateKey, to get the response of the API:
      import { Injectable, Inject, PLATFORM_ID } from '@angular/core';
...
import { makeStateKey, TransferState } from '@angular/platform-
browser';

import { isPlatformServer } from '@angular/common';
import { tap } from 'rxjs/operators';

export class TransferStateInterceptor implements HttpInterceptor {
constructor(
private transferState: TransferState,
@Inject(PLATFORM_ID) private platformId
,
) {}

intercept(req: HttpRequest<any>, next: HttpHandler):
Observable<HttpEvent<any>> {
const key = makeStateKey(req.url);

return next.handle(req).pipe(
tap((res) => {
if (isPlatformServer(this.platformId)) {
this.transferState.set(key, res);
}

}),
);

}
}
  1. Now that our state will be cached when the code runs in the server, let's make sure that, if the cached data exists, we can read it and then remove it so that when we are in the client application, the API can be called again on route change:
      import { HttpInterceptor, HttpEvent, HttpHandler, HttpRequest, 
HttpResponse, HttpHeaders } from '@angular/common/http';

export
class HeaderInterceptor implements HttpInterceptor {
...
intercept(req: HttpRequest<any>, next: HttpHandler):
Observable<HttpEvent<any>> {
const key = makeStateKey(req.url);

if (this.transferState.hasKey(key)) {
const response: any = this.transferState.get(key, {} as
any);
this.transferState.remove(key);
return of(new HttpResponse({
...response,

headers: new HttpHeaders(response.headers),
}));
}


...
}
}

Here, we are checking whether the cached data exists by using the hasKey method, and then we get the data by using the X method on the transferState service. Then, we remove it using the remove method and respond with the observable of the HttpResponse.

Now, let's run the build and serve our application again using npm run build:ssr && npm run serve:ssr before verifying that the API is not called after the page loads. You can also view the cached API response in the page view of the source, at the bottom.

In this section, we successfully transferred the state of the application from the server to the client using TransferState. Next, we will deploy our Node.js application to ZEIT Now and check the performance of our application.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.63.87