Firebase + Angular Universal = impossible is possible
- What firebase package to use in client and on the server?
- What mechanism use for asynchronous operations?
- How pass data from server to client avoiding requests duplication?
When we started developing our Angular Commerce, on of the main requirenments was SEO optimisation of the application for search engines. For this requirenment search crawler need to recognize content on visited pages. Server rendered applications return external data on user request embedded directly into HTML template. But when we deal with single page applications there are different asynchronous requests. So when you open source code of SPA page you will see something like this:
Search engine doesn’t know what content to index, because of missing content in fetched page, so our page can’t rich first pages of search engines.
We started to find solution for server rendening of SPA and stumbled upon
But we got stuck on the problem. Angular Universal doesn’t deal well with WebSockets. As we are using Firebase as backend for our Angular Commerce it was critical to us. So we want to tell about some pitfalls that we met in development and how we solved them.
Using client and server firebase
As we understood
client Firebase package is opening WebSocket for authorization, so Universal doesn't know when terminate server rendering. So we had an infinite loop on requesting some page.
We solved this by using
client Firebase package in client module and
node Firebase in server module. So providers in
app.module.ts look like this:
firebaseServer is presented in
server.ts. Also you must provide credentials key from
Now on server we are using
node Firebase package with
Firebase Realtime Database only and
client Angular is using
client Firebase package with all necessary functional.
Using Observables instead of Promises
As we noticed
Angular Universal doesn't deal well with
Firebase Realtime Database library query methods return promises in most cases. So we wrapped those method calls into
RxJs Observables. Here is an example of simple query to products bucket in
Firebase Realtime Database:
Passing server data to client
Angular Universal works? When user makes request, server run
renderModuleFactory method that gets use server build and immediately returns html template with rendered page. Then it starts to render browser Angular build. When rendering is over
Universal substitutes the view to the browser version. It's interesting that server and client build almost do the same work. It's noticeable in asynchronous requests to database when data is on view, than disappears (because
Universal substitutes the view to client version without data), and than appears again.
Here is demo with passing data from server to client:
And here without passing data:
Angular 5 in
@angular/platform-server module there is a
TransferStateModule. This module helps you to transfer it’s state from the server to the browser, to remove the need of requesting data again in the browser.
As we are working with
Angular 4, we discovered a solution how to pass data from server to client without
renderModuleFactory method have an optional argument
extraProviders you can pass providers that will be added to server module and will be present in server app. So we created a
serverStore object that will store all data that we want to pass to client app. Also providing object with setter of global
serverStore helped us setting it in server app.
app.engine callback in
server.ts will look like this:
Finally we set our
Window.store before importing other scripts. Getting server data is simple in AppModule:
Getting server data on client
What about using store in components? It’s simple. In server app get the data and set it to store. In client app we need to check if there is necessary data on store and get it. Here is a simple example how to get products from
Firebase Realtime Database in
serverStore provider only exist on
server build so it's injected with
@Optional decorator for avoiding error on client:
Angular Universal with
Firebase is fairly unobvious, but there are way how to deal with it.