Angular Universal and Firebase Hosting (Server-side Rendering with JavaScript Frameworks)
Articles,  Blog

Angular Universal and Firebase Hosting (Server-side Rendering with JavaScript Frameworks)


DAVID EAST: As a
server-side friend re– [BLEEP] Dang it! SPEAKER 1: Server-side friender. DAVID EAST:
Server-side friender. [BLEEP] It never just goes away. [MUSIC PLAYING] Hey, everybody. Welcome to the Angular
Universal episode in the server-side rendering
with JavaScript framework series. And in this episode,
we’re going to get up and running quickly
with Angular Universal. We’re going to be
using the Angular CLI to generate a
universal bundle, that is a core piece to getting
a server-side rendered app. Then, we’re going to be using
Angular Universal itself to take this universal bundle
and generate HTML and CSS. And then lastly, we’re going to
use Express to handle the HTTP requests to send back the
dynamically created HTML and CSS from Angular Universal. And in this video, we’re going
to be making an app called True Facts, which is really just
a simple list of facts that’s generated from an API call. Now, it’s not really
that complicated. But it’s a great example of
how server-side rendering can help improve your page load. So let’s dive down into
the laptop and get started. [BEEP] So I’m here in the
command line, and I’m going to use the Angular
CLI to create a new project. So I’m going to use,
ng New, ng true facts. And this will go and
install everything. And then I’ll open
it up in my editor. So open up the Source Folder,
an I will open up my app module, and import the
HTTP client module. So import from
Angular, slash common, slash HTTP, and
HTTP client module, and specify that in
my ng module imports. So we’ll go into
the component now, where I can actually go and make
a request with the HTTP client service. So I’ll import the
HTTP client service from the same place,
Angular, common, HTTP, and grab the actual service,
and we’ll inject that into the constructor. And from here, I can say,
this, dot HTTP, dot get. And I’ll use an endpoint
that points to the real time database. And now, I want to create
an observable of the facts that I’m retrieving,
which means I’ll have to import it from RXJS. So now, I can say, this
dot facts equals the facts that we were getting back. So at this point, I need
to go and render this. So I’ll delete all
the boiler plates, and I’ll create a UL with
an LI, use star ng 4, say, let fact of facts,
pipe it out to async, since this is a synchronous. And then, inside of here,
I can use fact dot text. And I’ll close out the LI. So now, I want to
go and add my CSS. So I’m going to go to
styles dot CSS, which is all the global
cells, and just paste in all of this basic CSS. So now that I have
my CSS, I’m going to initialize Firebase Hosting
to deploy out this website. So you’ll make sure to
install the Firebase tools. And then once you
have installed, use Firebase init hosting. I’m going to use the
non-SSR Angular project, because this won’t be set
up for server-side rendering initially. And then, now that
I have this set up, I can actually go out and I
can build my ng application– that’s ng build,
dash, dash, prod– and do a Firebase deploy. So now, I’m going to
open hosting site. So this is my
deployed application. And this is a non-server-side
rendered version. So if we go and
actually view source, we can see that we don’t
have any of the HTML that represents our app. It’s all just references to
Style Sheets and JavaScript. So now, if we want to set
up server-side rendering, we need to start off by
installing Angular Platform Server. And now that that’s done,
we can go to our app module. And we need to use the with
server transition method, and specify an
app ID that’s just something unique to the page. So now, I’m to create a
new file and call it app dot server dot module dot ts. And this is going to be an
ng module for the server. So I’m going to create
an ng module here. And we also want to import
from Angular Platform Server, and we’ll grab
the server module. And then, we need to
import our actual app. So we’ll import
our app components, and we’ll also import
our app module. And this way, we’ll be able
to create our universal bundle from this ng module. So we’ll create our
app server module, and we’ll import the app
module and the server module, and we’ll make sure that we
bootstrap up our initial app component. So now, we need to export
this with a main server ts. So the main dot server
dot ts is how we will export our app server module. But right now, we
don’t have a way to build this type
script to JavaScript. So we need to create a
ts config specifically for this universal bundle. So I’ll create a
new TypeScript file, call it ts dot config
dot server dot JSON. And I want to actually
extend from the ts config we have from before. So if you’re not
familiar with extends, it’s just how we can
take one ts config and then build off of it. So now, I’m going to
specify some compiler options where I want to say
my outDir is out dot TSC slash app. And that is what the Angular
CLI uses to build out to. And we need to set a base URL
for the current directory. And we need to specify that
the module’s common JS. And we want to exclude
any test files. So it’s really
important that you specify that the
module is common JS, because this is the module
format that Node.js needs to use, because currently,
our module format is ES 2015, and that is not
understood yet by Node.js. So TypeScript will
do us a big favor and convert that to common js. And then lastly, we need
to provide Angular compiler options. We need to specify that our
entry module is app slash app dot server
module, and use a hash to reach the actual class name. So now, with this
written, we can export from app slash app
dot server dot module, and export right out
our app server module. So now, what we need
to do is we need to go into dot Angular
dash CLI dot JSON And this is where we can
specify how our app actually gets built. So you
can see that there is this section here called apps. And currently,
there is one entry, and that is our client site app. This is the default that the
Angular CLI sets up for you. So what we want to do is
we want to actually create another entry. And this will be
built for the server. So what I’m going
to do is, I’m going to copy this object
right here, and I’m going to paste a new entry
and modify a few things. So I want to say that the
platform is for the server, and that the outDir
is actually called functions slash dist-server. And our main, we know, is
main dot server dot ts. We can get rid of polyfills. We need to change the ts config. And with that, we can go and
say, ng build, dash dash prod, dash dash app, 1,
because it’s an array, and 1 being the second
index in the array, we will build that specific app. So now, it’ll go
out and build it. And we have our server bundle. So if we go out to
our files, we can see in functions dist-server. So in dist-server,
we can see that we have our universal bundle
generated by the Angular CLI. So now that we have
our universal bundle, we need to start working
on our server code. So the first thing
I’m going to do is I’m going to initialize
Cloud Functions. So I going to use
the Firebase CLI and say, Firebase
init functions. So it’s going to go and do
some initial set up for us. And it’s going to ask
if I want to install the dependencies right now. So I’m just going to
go forth and do that. And now that that’s set up,
it has created an index js file in our functions folder. And for this example, I actually
don’t want to use JavaScript. I’m going to want to write
everything in TypeScript. So I’m just going
to delete this. And if I’m going to
write it in TypeScript, that means I’m going
to need a ts config. So inside of a
server folder, I’m going to create an index
dot ts for my server code, and a ts config dot
functions dot JSON. And then from here, I can
create my compiler options, and I want to compile
to common js for node. My target is going
to be ES 2015. And then my rootDir is
the current directory. And from here, I just want
to compile out the index ts file as my entry file. And lastly, I need
to specify an outDir, and I want it to go to
the functions directory, since that is where we will
deploy our server code. So now, in index ts, I’m
going to create my server. So first, I’ll import
from Firebase functions. And then, I’m going to
import from Express. And then, now, I need to import
from Angular Platform Server, and import the render
module factory function. And it’s this function
which will allow us to generate our HTML and CSS. And I’m also going to need to
import from the file system, so I can read files. And lastly, we need to
import zone js for node. And this is really important. So if you don’t
import this, this is going to totally
blow up on you. But this is what Angular
needs to know to do change detection on the server. So now, I’m going
to read my index dot HTML file from the file system. And when you get that from
the current directory– and make sure it
comes back as UTF 8– and so, to do that,
I actually just need to drag it
into my functions, so it reads it from
the correct directory. So now, I need to require
my universal bundle that was generated by the Angular CLI. And we know that was generated
in the dist-server folder. So I can require from the
dirname slash dist-server slash main dot bundle. And this require
will have an object that we can get called dot
app server module ng factory. And we can use this to
generate our HTML and CSS. So there’s a bit of a problem. When we generate our
universal bundle, it will be generated
with a hash– so main dot hash
dot bundle dot js. But we don’t need this
hash, because the hash is used for browser caching. And there’s no browser
caching needed on the server. So we actually can
get rid of this hash. So I’m going to open up
the dist-server folder. And I’m going to rename main,
so it’s main dot bundle dot js. And ideally, we’d want to
do this in an automated way. But for now, I’m just
going to rename things. So now that we
have our documents and our universal bundle, we
can go and create our server. So I’ll create an Express app by
calling Express as a function. And now, I’m going to
create an HTTP handler. And I’m going to
create a get route for star star, which means,
intercept every single route. And so, from here, I can use
the render module factory method and pass in my app
server ng module factory. And then I can use the document. And then I need to
get the current URL. And I can use that by using
the incoming request path. Render module factory
returns a promise So I’m going to call
dot then, and get the HTML that was generated. And now, from here, I can use
Express to send back this HTML. But I want to set caching. So I’m going to set a cache
control for max age 600 and s max age for 1200, which
effectively means that this content will be cached in
the browser for 600 seconds– or 5 minutes– and will be cached for 10
minutes on the CDN level. So this cache control
header is really important, because it keeps us from
having to go out to the server every time the user makes an
HTTP request for this document. So instead, we will either
deliver from the browser cache, or we can deliver from
the CDN level, which will be much faster, and will
keep the work off the server. Now, once the 1200
seconds expires, we’ll go out do
this process again. But for that time
frame, we will have our apps served from the cache. So now that we have
our server [INAUDIBLE],, I need to export the
function for Cloud Functions. Sp I’m going to call
it SSR app, and say, functions dot HTTPS on request,
and pass through the app. So now that that’s done, I need
to go into Firebase dot JSON and create my rewrite,
so Firebase Hosting knows about our function. We’ll use star
star as the source, and then our function name–
as we know, we call it SSR app, because right here,
export let SSR app. So now that I have my
server code written, I need to trans-pilot from
TypeScript to JavaScript. So I’m going to use the
TypeScript command line and say, ts dash p, and
use the server ts config. Now, right now,
I get this error. And this is only due to
the version of TypeScript I’m using, and the
version of Firebase admin. And hopefully, you
won’t see this error. But if you do, it’s benign. And we can actually
move past this. But hopefully, by the
time this video comes out, you won’t see it. So if we go to our
functions directory, we can see that our
index js is the common js version of our server code. So since we have this,
I’m going to serve, and I’m going to serve locally. So on local host 5,000,
you can see that we have our app up and running. And if we go and
view the page source, you can see that it
actually uses HTML that was server-side rendered. So this isn’t a lot of code. But there’s actually a
faster way of doing this. And we can use the npm module
called Angular Universal Express Firebase. And this is just a
little simple module I wrote to help make server-side
rendering your Angular apps a lot easier. So I also need to install this
into the Functions folder, because this will have
to be deployed out to Cloud Functions. And now, I can actually go and
delete almost all of this code. And I’m going to import star as
Angular Universal from Angular Universal Express dash Firebase. And rather than use
this, I can say, Angular Universal dot trigger. And then from here, I can
provide some config options. So I can say that
the index page is located at the current
directories index dot HTML. The main is located at
dist-server main dot bundle. I’m going to want
to enable prod mode. I’ll set a CDN cache
expiry for 1200, and the browser
cache expiry for 600. So I want to go and deploy
this app to production. But before I can do
that, I need to make sure that I have all the needed
modules in the package JSON in my Cloud Functions folder. And that’s because when you
deploy your app to Cloud Functions, it’s going to install
all the dependencies needed to run your server code. And if it’s not listed
in that package dot JSON, then it’s going to have an
error and not be able to run. So a simple way to do that
is, I go into my main package dot JSON for my Angular CLI app. And I go and I copy all
of these dependencies. And now, I’ll go to my
functions, package dot JSON, and I just paste it in. And then also, I’m
going to need Express. So now, I’ll open up the
terminal, CDN to functions, and install. And then now, I can deploy
by using Firebase deploy. So now, our app is deployed. And so, I’ll go and
visit this in my browser. So now, here in
production, you can see my URL is SSR dash Angular
dot Firebase app dot com. And we have our
app up and running. And if I view the
page source, you can see that we have our
server-side rendered content. [BEEP] So that’s how you set up Angular
Universal on Firebase Hosting. In the next video,
we’re actually going to go and profile this
website using the Chrome DevTools and Webpagetest. And we’re going to
see how it performs compared to the non-server-side
rendered version. So make sure to subscribe
so you are notified when that video gets out. So that’s all for today. If you like this
video, make sure to hit that thumbs up button. And otherwise, I will see
you in the next video. In this project, I’m going to be
building a little app called– [GROAN] that’s not
what it’s called! [BLEEP] We are inside the computer. [BLEEP] All right, this is it. Just keep rolling. We’ll do it live!

88 Comments

  • Val Allen Samonte

    Also, I would love to see the Firebase SDK to have offline first support for PWAs (offline like caching the fetched data from firebase)

  • David East

    Hey everyone! If you to learn more about profiling Angular Universal performance I wrote an article here: https://davidea.st/articles/measuring-server-side-rendering-performance-is-tricky

  • Tsortanidis Christos

    Hey David, at 16.10 we have to add express too or it's bundled in the package you wrote?
    And also I'm failing with a “` Error: Error parsing triggers: Cannot find module '@angular/core' “` even though I have installed all dependencies

  • tntg5

    It's incredible how complex it is to make Node generate your HTML. In this example, there are no Dom manipulations.
    As soon as you add libraries such as Angular Material, NgPrime, or NgBootstrap, things start to go out of controle.
    It's amazing to see that Google is pushing a technology (Angular) that is by design incompatible with how SEO works.
    Come on google, I am sure you can make SSR as easy as :
    ng build –ssr –prod 🙂
    Without the hassles of configuring it

  • Mark Pieszak

    For those using the Angular CLI, we have just updated the universal-starter so you can get started right away 🙂
    Find it here: https://github.com/angular/universal-starter/tree/master/cli

    I also made a PR and the CLI wiki will be updated so you can follow along with each step needed to get you up and running quickly. Find the PR here: https://github.com/angular/angular-cli/pull/7796/files

    Hope that helps!

  • Nayan Hathiwala

    Hi David (+firebase), I'm using UIKIT for my design framework, I have npm installed uikit package and added the references to 'styles' and 'scripts' in .angular-cli.json file. My app rendering state is SSR, which compiles well but it fails to package in my all extended scripts which I had added in .angular-cli.json file. I have tried non-ssr project which executes with no issues. Please, can you assist me with what might be wrong or can do to make it work.

  • cutiko

    Im really looking forward for the React version of this, kind of got confuse with the Angular code. When you do the React version can you please reinforce the Firebase work thar has to be done? Please 🙂

  • Michael Krzenski

    Kudos to the presentation – very well delivered. However, Uhhh… omg I find this just a bit complex for what we're trying to accomplish.  I think I'll skip server side rendering for now and come back around when we have more convention, etc. in place. 🙂 Good job on the angular-universal-express-firebase module though it's a very good step in the right direction. 🙂

  • Martin Nuc

    I followed your instructions and my issue is that the express app is not called. Should I setup something else except rewrite in my firebase.json? Also when I try to execute function manually using function's URL I get "Billing account not configured. External network is not accessible and quotas are severely limited. Configure billing account to remove these restrictions".

  • Alberto Ardito

    There's an error in the "server/index.ts" file. The correct directory for the "index.html" is "/dist-server/index.html" and not just "index.html", so the correct "document" variable value is:

    const document = fs.readFileSync(__dirname + '/dist-server/index.html', 'utf8');

    Obviously you need to generate again the "index.js" file in the functions folder using "tsc".
    With this correction everything works.
    Thank You!!

  • Федор Усаков

    @Firebase Failed to complete. After trying to start locally(firebase serve –only functions,hosting) I get 'moduleFactory.create is not a function' with latest angular cli and angular5

  • Danilo Morales

    Dear David,
    This is great for a single page site. I have a multiple pages website and if I try to go to the page manually or refresh the page, it gives me an error. "Error: could not handle the request". I tried to put ' "destination": "/index.html" ' and then gives me this error ' Error: HTTP Error: 400, hosting.rewrites[0] is not exactly one from [subschema 0],[subschema 1] '. Sorry to ask here but apparently, I am the only one having this issue. I have not been able to find an answer anywhere. Could you please help me with this? Thanks.

  • Leonid Ramirez Ochante

    How would you work now that the default is angular5? Since it mentions that it comes with tools to be able to make the server side rendering.
    "In 5.0.0, the team has added ServerTransferStateModule and the corresponding BrowserTransferStateModule. This module allows you to generate information as part of your rendering with platform-server, and then transfer it to the client side so that this information does not need to be regenerated. "
    Besides that now we also have firestore.
    https://blog.angular.io/version-5-0-0-of-angular-now-available-37e414935ced

  • Matt Carbone

    David, many thanks for the video. I got your example to work. It would be nice if there was a script we could put into our package.js to help with some of the steps. For example, in one of the step you dragged the index.html file from dist to dist-server was very quick. It was hard to see the drag in the video. I had to play the video back and it was still not so easy. Maybe instruction slide would assist. It would be great to have a future video series example developed with Angular 5 with Angular CLI ^1.6. for PWA, service worker and push notification incorporating Angular Universal. Thanks again.

  • Crewnie Master

    It is amazing to be able to compile your applications made in angular on the server.
    I am very excited because this keeps growing.

  • Pierre Chavaroche

    Does AngularFire2 with Firestore supports Angular Universal? That would be very important for us to know.
    Thank you for the great video!

  • Ridhwan Zaidi

    Hello, I have an error on server/index.ts.. It stated that cannot find module 'firebase-functions', can somebody help me with it?

  • TCHOUTANG BANKOUE MICHEL FRANKLIN

    hi guy!! i like what you do a lot. please could you do videos and tutos on vuejs & firestore?!! i really need your take that area. thanks in advance

  • Stanley Eosakul

    I was trying to code along to this video, but he's so fast! I couldn't get his code to work correctly so I took bits and pieces from this video and other sources and got it to work using Angular 5 and Angular CLI v1.7 which has native Angular Universal support! Github repo here: https://github.com/stanleyeosakul/angular-universal-firebase, and I included a step-by-step guide.

  • Martí Crespí

    Hi, after build app 0 and app 1, I can't extract AppServerModuleNgFactory from main.bundle.js! I have my app splitted in 4 modules, an inside main bundle I can find them, but My AppServerModuleNgFactory is missing. Someone can help me? I am using angular 5, and I have read than it doesn't generate ngFactory.ts files!

    Thanks!

  • polypus74

    Is this still the situation today? Is there some official solution? I am evaluating angular and this makes me want to run the other way screaming.

  • Jitendra Kumar

    How to deploy Angular 6 + universal on firebase? Can you please explain step by step or provide any helpful link?

  • Camilo Elgueta Basso

    it still works with Angular 6 🤔 or there is a new way of doing SSR with angular 6 + firebase hosting?

  • Alexon da Silva Moreira

    I want to put this into practice, now I could find this same code, so I need to type for example css

  • David Heisner

    does this require an actual external server that firebase doesn't offer? I followed everything exactly, but the page source was still hidden behind the javascript, also is this still the current solution to getting angular to be read by google bot? I'm just getting into making my sites SEO friendly and learning about all this

  • Abhijeet Bharti

    hello
    I am trying to connect to connect custom domain with firebase web hosting but it always shows "needs setup".
    Please help me

  • Federico Moreno Rodríguez Chalbaud

    #AskFirebase #AskDavidEast Hi David! Many thanks for you videos. We are struggled with the SRR implementation, and it seems that is beacause an incompatibility with angulafire2. I have this error message: "Cannot read property 'filter' of undefined at _firebaseAppFactory", "The XMLHttpRequest compatibility library was not found" "Firestore has already been started and persistence can no longer be enabled server side rendering" "cannot read property host of undefined" (the line of window.location.host). Do you have any idea about this issue?

  • Ingeniero en Computación Alejandro Javier Villalón Navarro

    In angular 6 everything is different.
    Will we have an updated video so that we can achieve the server side render in firebase functions with angular 6?
    Please help us with that.

  • Vishal Garg

    I am not sure why people in google can make such type of videos as this guy is doing very fast changes and its useless to create videos like these with configuration setups. There should be auto initialization of this code with angular CLI. It just create complexity and negativity in my head.

  • kartik watwani

    I cannot build with command `ng build –prod –app 1` because structure has changed in angular 6 now it is an object inside architect as instructed on https://angular.io/guide/universal. Can you expalain on how to do it now ?

  • himani bhardwaj

    Hi,
    I want to deploy my angular universal code on a2 shared hosting. Could you please make a session on that. It would help me. Thanks

  • Lạng Hoàng

    Anyone here still struggle with this till now? I'm trying to implement it but got error with firestore (i'm not using cloud function for some reason).
    The error was 'Cannot read property 'stringify' of undefined'. I already post a question here: https://stackoverflow.com/questions/53994844 and on firebase comunication slack but get nothing till now. It would be great if you can give me a hand!?

  • Michael Tangen

    I've labored over Universal 6 and trying to get it to work in Firebase for the past couple days now following tutorials on the online documentation, blog posts, and other tutorials out there and there's nothing quick or easy about this. In fact, I still can't get it to work (at all). At this point, it almost seems a better use of my time to just make the entire site in static HTML for the sake of getting Google's bots to actually read the web content instead of trying to serve it up with Angular and not be read at all.

    It would be IMMENSELY helpful (and I cannot understate this) if Angular and/or Firebase could streamline the process of doing what Universal is supposed to do without all this nonsense. The site I'm working on uses Angular heavily but if it can never be read by Google Search Bots or social network sharing, I hardly see the point of using Angular at all. It would be nice to see Angular and Firebase work together to address this with a more streamlined solution, one that doesn't require hours of configurations and days of tearing your hair out because things just don't work the way people say they will in their documentation, tutorial, or blog post.

Leave a Reply

Your email address will not be published. Required fields are marked *