Angular Server Side Rendering (with hosting)
Written by © Maxence RABALLAND, 2021, My website.
Based on Angular documentation and Angular universal docs. More information about hosting and pricing on firebase.
Prerequisites
You’ll have to create a firebase account first. Then install npm (find on node.js website) and Angular on your local machine :
Then, create your Firebase project. You have to enable firebase hosting and firebase functions. When you create your buckets, make sure to choose the right region (e.g., eu-west3, us-west1, …).
To enable firebase functions, you’ll have to switch to a firebase blaze plan. It’s the pay-as-you-go plan. For more info on pricing, visit the firebase pricing page.
Try this project in your environment.
Run the following commands on your command-line tool :
git clone https://github.com/maxencerb/Simple-ssr-Angular.git
cd Simple-ssr-Angular
npm i
You can open your code editor code .
and run the angular app locally with ng serve --open
or run the dev server with npm run dev:ssr
Create the project
To start, use the angular CLI tools to create a simple project. I’m am using the latest version of angular and angular universal to date (Feb. 2021).
ng new my-app
cd my-app
code .
This will create a simple angular project and open your favorite code editor. During development, you’ll be able to use the ng serve --open
command to serve your app locally and test with hot reload.
Add angular universal
With the latest angular version, there is now a built-in angular command that adds the packages and remodels your folder structure.
ng add @nguniversal/express-engine
You’ll be prompted with the following :
So, this will create the following folder structure :
Now you can run your server locally with npm run dev:ssr
.
Add to firebase project
First, create your firebase account and create your project. Then install the firebase CLI with npm and log in to your firebase account :
npm i -g firebase-tools
firebase login
This will prompt you with a browser to sign in to your google account.
For CI (continuous integration), refer to the firebase docs and login with firebase login CI
.
Now add firebase to your project :
This command will detect that you are on the angular universal project. Wen asked if true, say Yes.
Then choose the Firebase project you created for your app :
Once done, you’ll be all set.
Deploy
The last step is to deploy your app. Just run the command :
ng deploy
This will build your project for production and deploy to firebase functions.
Development Tips
Faster development
When using the server-side rendering local server, each time you modify your files, it will rebuild parts of the server. It can take a long time before it refreshes in your browser.
If you are modifying your angular app only, I recommend using the ng serve
command. The hot reload is much faster.
Then, if you want to develop server features or debug your whole app, use npm run dev:ssr
. It takes longer to refresh, so use it only if necessary.
Create a component, module, etc.
As you can see, in your src/app
folder, there is now 2 app module : app.module.ts
for your app and app.server.module.ts
. When creating a component or a service, angular will want to auto-import to an app module. So in the CLI, you'll have to specify which one to choose. You might want to use app.module.ts
in most cases.
To create a component, enter :
ng g c new-component --module app or ng g c new-component --module app.server
The same applies to modules and services.
Create a 404 Page
When you request a non-existing route while using server-side rendering, the server is trying to create an HTML template with a non-existing angular route and create a useless workload for your server.
Create a NotFoundComponent :
ng g c components/not-found --module app
Make sure your router-outlet
markup is top-level on your app.component.html
<!-- app.component.html --><router-outlet></router-outlet>
<!-- not-found.component.html --><h1>404 - not found</h1>
Feel free to add your own content and styling. Now add this to your app-routing.module.ts
:
// app-routing.module.ts
import { NgModule } from '@angular/core';import { RouterModule, Routes } from '@angular/router';import { NotFoundComponent } from './components/not-found/not-found.component';const routes: Routes = [ // your routes here {path: '404', component:NotFoundComponent}, {path: '**', redirectTo:'/404'}];@NgModule({imports: [RouterModule.forRoot(routes, {initialNavigation: 'enabled'})],exports: [RouterModule]})export class AppRoutingModule { }
It is important to add your custom routes before these because they will search for the first matching pattern.
Custom express.js API endpoints
You can make modifications to your server settings in the server.ts
file. To add custom API endpoints, add them before the built-in endpoints. This will overwrite them.
// server.ts...export function app(): express.Express {const server = express();const distFolder = join(process.cwd(), 'dist/<your-project>/browser');const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';server.engine('html', ngExpressEngine({bootstrap: AppServerModule,}));server.set('view engine', 'html');server.set('views', distFolder);// Add custom endpoints hereserver.get('/api/hello', (req, res) => { // response example res.send(`hello from maxence, the current time is ${Date.now()}`);});// End custom endpoints// built-in endpointsserver.get('*.*', express.static(distFolder, {maxAge: '1y'}));// All regular routes use the Universal engineserver.get('*', (req, res) => {res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });});return server;}...
View project on http://github.com.