I was recently asked to upgrade the existing Angular app with a new Angular SSR framework. However, what I didn't know at the time is that Angular SSR only works on Angular 17. So I'm writing this guide that will teach you how to upgrade your existing Angular app to v17 as well as add server-side rendering.
Edit: Upgrade from 17 to Angular 18
Node.js Compatibility š
Since Angular depends on Node.js, you need to have compatible versions of both. Thatās why the Angular team has this compatibility table to let you know which version of Node.js is compatible with the version of Angular.
If you need to upgrade your Node.js version, be sure to go to Nodejs.org and download the recommended version or use the NVM instead:
> nvm install 18 # Install Node.js v18
> nvm use 18 # Switch to installed version
# Now using node v18.x.x
Since I was using Angular 16 with Node.js 18, I didn't need to change my Node.js version.
Ng Update š¹ļø
The easiest way to upgrade to a newer version of Angular is to execute the ng update
command in your CLI in the project folder.
Upon running the command you'll get tips on what to upgrade, e.g.
> ng update
You need to manually upgrade the following:
Angular CLI => ng update @angular/cli
Angular Core => ng update @angular/core
Angular CDK => ng update @angular/cdk
...
Angular CLI
To get started, upgrade your current CLI version to the latest one:
> ng update @angular/cli
Upon updating the CLI you might be prompted to commit your changes to the Git before proceeding.
Angular Core
The next step is to upgrade the Angular framework, which you do using:
> ng update @angular/core
The core framework is now upgraded to v17 which you'll notice in the dependencies list in your package.json file.
Angular Material
Since I'm using Angular Material in my project I needed to upgrade it before continuing with the rest of the updates.
> ng update @angular/material
Angular CDK
With Material sorted out, I installed the last mandatory update (on the previous list) which was Angular CDK:
> ng update @angular/cdk
Now the project should be fully migrated to the latest version.
For some reason, I also needed to fix the paths of all the imports of my custom components, services, models, etc, and TSConfig errors (which you can solve by copying the tsconfig from here), but this is a pretty small complaint.
Adding Angular SSR š
The new Angular/SSR package replaces the Angular Universal. The new framework is faster and more intuitive.
> ng add @angular/ssr # installs Angular SSR
For those of you coming from Next.js, Angular uses Express.js to handle the backend routing and CommonEngine
(from Angular/SSR) for rendering an Angular app on the Express.js server.
Fixing Config Imports
Upon setting up the Angular SSR framework, Angular will create a server.ts
(Express.js file that loads the app) and a main.server.ts
file (that is equivalent to Angular App Module but for the server).
You may run into an issue with the current Angular config not knowing the correct path of the two newly added files.
Make sure to set this properties to current paths:
angular.json/build/options
"server": "src/main.server.ts", // path to your file
tsconfig.app.json
"files": [
"src/main.ts", // path to your file
"src/main.server.ts", // path to your file
"server.ts"
],
HTTP Client Configuration
Upon running the HTTP services with SSR, Angular might throw this error:
NG02801 is not an error but a warning that encourages the developers to enable the fetch implementation of the HttpClient.
To resolve it, you need to add provideHttpClient(withFetch())
to your app.config.ts file or app.module.ts (if you're using the old NgModules):
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { provideClientHydration } from '@angular/platform-browser';
import { provideHttpClient, withFetch } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideClientHydration(),
provideHttpClient(withFetch()), // here it is!
],
};
Connecting Frontend & Backend
As a proof of concept, create a dummy endpoint in the server.ts
file:
server.get('/api/greetings', (req: express.Request, res: express.Response) => {
res.send({ message: 'Hello from Angular SSR!' });
});
Generate a new Angular service using:
> ng g s services/greetings
And connect to your API via HTTP Client:
import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
interface IGreetings {
message: string
}
@Injectable({
providedIn: 'root'
})
export class GreetingService {
// alternative to the class constructor
private readonly httpClient = inject(HttpClient);
getGreetings(): Observable<IGreetings> {
return this.httpClient.get<IGreetings>('/api/greetings');
// notice how you don't need to prefix your route with localhost:PORT
// because it's coming from the same domain
}
}
Create a button element inside the App Component (or any other component):
<button (click)="sayHello()">Say Hello!</button>
And register a handler for this button within the TypeScript file. This handler will invoke the service you've just created.
import { Component, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterOutlet } from '@angular/router';
import { GreetingService } from './services/greeting.service';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, RouterOutlet],
templateUrl: './app.component.html',
styleUrl: './app.component.scss'
})
export class AppComponent {
private readonly greetingsService = inject(GreetingService);
sayHello() {
this.greetingsService.getGreetings().subscribe(console.log)
}
}
Running the App
Firstly build the app using the build script from the package.json file:
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test",
"serve:ssr": "node dist/<your-app>/server/server.mjs"
},
> ng build # or npm run build
This will contain a dist
folder in your main directory containing browser
and server
directories.
Then run the server and the client using the server:ssr
script:
> npm run serve:ssr
> <your-app>y@0.0.0 serve:ssr
> node dist/<your-app>/server/server.mjs
Node Express server listening on http://localhost:4000
If you then click the button on UI, you should see a message from the API printing in the console.
And that's it!
Why Angular 17 š
Angular 17 is a new iteration of the popular frontend framework made by Google. Version 17 ships with a host of exciting new features:
- New control flow
- Deferrable views
- New Server-Side-Rendering framework
- New Docs
- Signal-based component inputs and two-way binding, just to name a few.
That's all from me today.
If you'd like to learn more about Angular, make sure to check out my other blog on Medium and follow me on Twitter to stay up to date with my content updates.
Bye for now š
Top comments (2)
Hi Mirza Leka,
Your tips are very useful
Thanks for sharing
Thanks @jangelodev !