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

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
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!
Info
Channel: Firebase
Views: 45,345
Rating: undefined out of 5
Keywords: server side rendering, server side rendered apps, rendering, firebase, fast rendering, fast render, html, fast first render, javascript, css, app.js, server-side rendering, angular, react, rendering code, javascript frameworks, javascript framework, firebase hosting, firebase developer, developer, dev, time to interactive, product: Firebase, fullname: David East, Location: MTV, Team: Scalable Advocacy, Type: DevByte, Other: NoGreenScreen, GDS: Yes;
Id: gxCu5TEmxXE
Channel Id: undefined
Length: 17min 20sec (1040 seconds)
Published: Fri Sep 22 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.