Skip to content

Instantly share code, notes, and snippets.

@debojyoti
Last active October 21, 2022 08:51
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save debojyoti/d75a517f77d8e5881d87553f1e493614 to your computer and use it in GitHub Desktop.
Save debojyoti/d75a517f77d8e5881d87553f1e493614 to your computer and use it in GitHub Desktop.

Angular Universal setup for firebase hosting

1) Install firebase-tools globally

npm i -g firebase-tools

2) Install @angular/platform-server

npm i --save @angular/platform-server

3) Modify BrowserModule in app.module.ts

imports: [
    BrowserModule.withServerTransition({ 
      appId: 'project_name' 
    }),
    AppRoutingModule
  ],

project_name can be found in angular.json

"projects": {
    "project_name": {
      "root": "",

4) Create a new file app.server.module.ts in src/app

Paste the following code

import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';

@NgModule({
    imports: [
        ServerModule,
        AppModule
    ],
    bootstrap: [AppComponent]
})
export class AppServerModule { }

5) Create a new file main.server.ts in src

Paste the following code

export { AppServerModule } from './app/app.server.module';

6) Create a new file tsconfig.server.json in src

Paste the following code

{
    "extends": "../tsconfig.json",
    "compilerOptions": {
      "outDir": "../out-tsc/app",
      "baseUrl": ".",
      "module": "commonjs",
      "types": []
    },
    "exclude": [
      "test.ts",
      "**/*.spec.ts"
    ],
    "angularCompilerOptions": {
      "entryModule": "app/app.server.module#AppServerModule"
    }
}

7) Modify angular.json

7.1) Add server block after lint, within architect

"lint" : {
    ...
}, "server": {
      "builder": "@angular-devkit/build-angular:server",
      "options": {
        "outputPath": "functions/dist/server",
        "main": "src/main.server.ts",
        "tsConfig": "src/tsconfig.server.json"
      }
    }

7.2) Modify build > options > outputPath

"build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "functions/dist/browser",

Step-8) Build browser and server code

ng build --prod
ng run project_name:server

project_name is same as Step - 2

Step-9) Build browser and server code

ng build --prod
ng run project_name:server

Step-10) Init firebase

firebase init

10.1) Select functions and hosting 10.2) Select your project from firebase console 10.3) Select Javascript for cloud functions 10.4) ESLint: No 10.5) Dependency install now: No 10.6) public directory: public 10.7) Configure as a single-page app: Yes

Step-11) Modify package.json in functions directory

11.1) Remove scripts block 11.2) Add all dependencies from angular project's package.json (In root directory) Example

{
  "name": "functions",
  "description": "Cloud Functions for Firebase",
  "dependencies": {
    "firebase-admin": "~6.0.0",
    "firebase-functions": "^2.1.0",
    "@angular/animations": "~7.1.0",
    "@angular/common": "~7.1.0",
    "@angular/compiler": "~7.1.0",
    "@angular/core": "~7.1.0",
    "@angular/forms": "~7.1.0",
    "@angular/http": "^7.1.4",
    "@angular/platform-browser": "~7.1.0",
    "@angular/platform-browser-dynamic": "~7.1.0",
    "@angular/platform-server": "^7.1.4",
    "@angular/router": "~7.1.0",
    "core-js": "^2.5.4",
    "rxjs": "~6.3.3",
    "tslib": "^1.9.0",
    "zone.js": "~0.8.26"
  },
  "private": true
}

Note: Versions and list of dependencies will be different in your project

Step-12) Install function

12.1) From the root directory of angular project, run

npm --prefix functions install

Step-13) Modify index.js inside functions directory

Replace the content by the following code

require('zone.js/dist/zone-node');

const functions = require('firebase-functions');
const express = require('express');
const path = require('path');
const { enableProdMode } = require('@angular/core');
const { renderModuleFactory } = require('@angular/platform-server');

const { AppServerModuleNgFactory } = require('./dist/server/main');

enableProdMode();

const index = require('fs')
  .readFileSync(path.resolve(__dirname, './dist/browser/index.html'), 'utf8')
  .toString();

let app = express();

app.get('**', function(req, res) {
  renderModuleFactory(AppServerModuleNgFactory, {
    url: req.path,
    document: index
  }).then(html => res.status(200).send(html));
});

exports.ssr = functions.https.onRequest(app);

Step-14) Modify firebase.json in root directory

Replace the content by the following code


{
  "hosting": {
    "public": "public",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source" : "**/*.@(css|js)",
        "destination": "/index2.html"
      },
      {
        "source": "**",
        "function": "ssr"
      }
    ]
  }
}

Step-15) Create build files link in the public directory

Run from root directory

cp -a functions/dist/browser/. public/
mv public/index.html  public/index2.html 

Step-16) Try in local environment

firebase serve --only functions,hosting

Step-17) Deploy!

firebase deploy

Bonus trick

1: There will be some cases where you may need to run different code while rendering in server side.

You can detect if the page is loaded in browser or not. Just modify the component's code as below

import { Component, Inject, PLATFORM_ID, OnInit } from '@angular/core';
import { isPlatformBrowser, isPlatformServer } from '@angular/common';
...

@Component({
  ...
})
export class AppComponent {

  ...
  
  constructor(
    ...,
    @Inject(PLATFORM_ID) private platformId: Object,
    ...
  ) {
  }

  ngOnInit() {
    ...
    if (isPlatformBrowser(this.platformId)) {
        window.location.replace('http://sidanmor.com');
       } else {
        //Server only code.
       }
       ...
  }
}
@newPrimitives
Copy link

Amazing tutorial!

@basvdlee
Copy link

Thanks alot, great tutorial!

@stupidly-logical
Copy link

This was great! Thanks.

@lennon182
Copy link

Hi... This don' t work anymore on Angular 9... i get this error:

"TypeError: Cannot read property 'moduleType' of undefined"

Regards...

@arunw3
Copy link

arunw3 commented Apr 27, 2020

Any tutorial for Angular 9 ?

@gustavo-alarcon
Copy link

gustavo-alarcon commented May 9, 2020

Hi, @arunw3 !
I think this tutorial is what you are searching for: SSR - Angular 9, hope this helps you !

@nourabbas96
Copy link

Any tutorial for Angular 9 as the function in firebase not working

@Josee9988
Copy link

With angular 9 it gives: TypeError: Cannot read property 'moduleType' of undefined

@Natsu29
Copy link

Natsu29 commented Nov 16, 2020

There have been some changes in functions/dist/server/main.js. there is no exports named AppModuleNgFactory. so it's showing an undefined.

@RezaRahmati
Copy link

what is the difference between step 8 and 9?

@Ross-Rawlins
Copy link

How do you fix the issues of using require with newer builds complaining and prompting you to move to dynamic imports.

@Noext
Copy link

Noext commented Jun 27, 2022

not working with angular 14

same error as @Ross-Rawlins

@broslukasz
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment