Angular SSR: The Browser Is Not The Server
In this article, we're talking about blazing fast performance with Angular Universal with server-side-rendering (SSR). One of the great things about SSR is that we get to use the same code on our frontend and our backend to render our app. Well, sort of. When we use the same code, right off the bat we have a problem: the browser is not the server and there are differences between what we can do in each environment. The benefit of rendering our Angular app on the server is that we can fetch data privately and efficiently before we send anything to the browser. Our server is (in this case) Node.js, and so on the server we can use: But the browser is not the server. And if we try to call browser-only APIs, then we'll break SSR. Well, three things come to mind that are exclusive to the browser: While it's awesome that our Angular application can share code between the server and the browser, if we want to use any of these objects, we need to execute a different logic path based on the current runtime: Node.js or the browser window. Below, I'm going to show you one of the techniques for doing that. Let's add internationalization to your application. Let's display product prices in three currencies: US dollars, British pounds, and Polish zloty. The application should pick a currency based on browser settings, and if a given language is not supported, it should fall back to Polish zloty. Let's generate a new service: Now let's detect user language and implement the getCurrencyCode() method that returns one of the three available currency codes: Now in one of our components, say ProductDetailsComponent , we can use this service to get the user's currency: Then we could use the userCurrency in a view with the currency pipe: From now on, prices should display in a currency defined by the user's localization settings. This is great, right? Well, no. Unfortunately, this logic breaks SSR: It would help if we had a mechanism to detect whether the current runtime is the browser or the server - and thankfully that's why we have isPlatformBrowser() and isPlatformServer() : Angular ships with the isPlatformBrowser() and isPlatformServer() methods in the @angular/common package. Each of these methods accepts one parameter: the platform ID. It can be retrieved via the Dependency Injection mechanism using the injection token PLATFORM_ID available in the @angular/core package. So to change our internationalization service I18nService above, Add these new imports: Modify the service constructor to only use the window object if an instance of the service executes in the browser: This should be enough for SSR to start working again but we don't get internationalization pre-rendered on our server-side render -- internationalization won't appear until after the app loads. So what we need is a way to know what language to render from the origin HTTP request to the server . The question now is how to retrieve information about user language on the server. Is it even possible? Yes, it is. When you're performing a request from the browser, the browser adds a bunch of HTTP headers that you might not usually think about. One of these headers is Accept-Language which tells us what language the user wants! For example, the header might come through like this: Accept-Language: en-US,en;q=0.5 . Angular Universal allows you to get an object that represents an HTTP request. It's available via Dependency Injection under the REQUEST token from the @nguniversal/express-engine/tokens package. The Request object contains the following fields: So we update our imports by adding the Request object, the REQUEST injection token, and the Optional decorator: Change the constructor to inject the Request object and retrieve user language from the Accept-Language header: If we use a tool like curl or Postman, we can verify that language settings are working within the pre-render (or fall back to Polish zloty when the Accept-Language header is missing): Angular Universal is a super-powerful tool in your Angular toolbox -- but if you're just used to writing frontend Angular, you might find it a bit tricky. But with a little guidance, you'll find you can get blazing-fast Angular apps. Check out the course Mastering Angular Universal: Build Blazing-fast Server-Side-Rendered Angular App s