Getting your Angular Universal site production ready
So I've got to admit, when I realised a couple of years ago that a plain Angular application wouldn't render dynamically-generated metadata, I was pretty surprised. I kind of felt like it was one of those huge oversights like you sometimes hear about on big civic projects where they build a tram but forget to have any platforms or something like that. Surely half the internet is metadata? And surely half the point of JS frontend frameworks is to get data out of an API and render it? But anyhow... Angular Universal to the rescue.
I've recently finished converting some of our plain Angular pages to Angular Universal, and I decided to do the same with my blog so that I could put links to it on social media. Here's how I did it.
Convert your app to Angular Universal
I've based a fair amount of the first part of this on this tutorial from Twilio.
In summary, if you've already got AWS CLI set up, and you've got an Angular application you'd like to make Universal, you can accomplish it in a couple of steps:
# convert your application to use Universalng add @ng-toolkit/universal# set it up so you can deploy it to serverlessng add @ng-toolkit/serverless# deploy it to serverless!npm run build:serverless:deploy
There's quite a lot more in the Twilio article, but I don't want to steal someone else's thunder. It's pretty cool - you'll end up with a Lambda function that runs an Express server to serve your Angular Universal site, an S3 bucket with the site in it, and API gateway to serve it over, and a CloudFormation template to orchestrate the whole thing. The trouble is, your site's got a URL like this:
And who wants that?
(You may also find, depending on whether your application makes use of the browser APIs, that the conversion to Universal isn't quite as easy as all that. Generally it's probably easier to make the decision to do it this way earlier on in the life of the project. But people always say that, don't they?).
Getting it production-ready
We're going to do a few things in this part:
- Create a CloudFront distribution for our API endpoint
- Tidy up the code so it will work with the CloudFront distribution
- Put everything into a Bitbucket Pipeline so you can deploy it nice and easily
AWS CloudFront is a content distribution network, which in brief means it will cache versions of your app in AWS infrastucture around the world where it's likely to be the most useful. I suppose if you wanted to you could configure your DNS to point directly at your API endpoint, but the performance can be a little sluggish, plus you may want to serve other applications under the same domain, so you'll need a way to route your traffic. There is a charge for CloudFront, but it's fractional - on the free tier it's 50 GB of data transfer out, and 2,000,000 requests per month.
The AWS documentation on setting up CloudFront is actually pretty great. If you don't already have an AWS account, set one up (you'll have to give them your credit card details but don't worry, there are very few things you can use that will cost a lot of money and there are plenty of ways to make sure you don't spend more than you want to). Here's a couple of gotchas that took me a while to figure out:
- Log into your account and go to CloudFront. Click Create Distribution. There's a couple of things you'll need to pay attention to here. The origin is going to be the long, ugly API gateway endpoint URL that got generated when you ran the serverless deploy command earlier, minus the /production/ path. That's what you're going to add as the origin path
- The Alternate Domain Name (CNAME) needs to be the same as the domain you've got configured in Route 53
- The Enable IPv6 field is automatically checked. If you leave it checked, you'll need you'll need to create two alias records for the distribution, one for IPv4and one for IPv6. The AWS documentation describes how to do this
Save your Route 53 changes and save your CloudFront distribution. The changes can take twenty minutes or so to propagate so maybe now's the time for a celebratory coffee/beer/Coke/walk around the block.
This is starting to get quite long, so I'm going to cover setting up the application itself and configuring your Bitbucket Pipeline in a different post.