Note: this guide explains step by step how to add Firebase to PWA Starter Kit.
If you already have some Firebase knowledge and you just want to get everything ready out of the box, you might want to checkout the Firebase branch on the PWA Starter Kit repo, that already contains all the needed files.
Firebase Hosting alone is not sufficient for hosting the prpl-server build since it requires some server-side processing of the user agent string. Instead, you will have to use Firebase Functions for that.
-
Head over the Firebase Console and create your project. Make note of the project ID associated with your app
-
Install Firebase Command Line Tools:
$ npm i -g firebase-tools
-
Log into your Firebase account from the CLI by running:
$ firebase login
-
Set the default Firebase project for your PWA. You can do that by either running this command:
$ firebase use --add <project-id>
Or by creating a file named
.firebaserc
in the root of your application, containing:{ "projects": { "default": "<project-id>" } }
-
Create a file named
firebase.json
in the root of you PWA and paste this JSON in it:{ "hosting": { "public": "build", "ignore": [ "firebase.json", "**/.*" ], "rewrites": [ { "source": "**", "function": "app" } ], "headers": [ { "source" : "**/service-worker.js", "headers" : [ { "key" : "Service-Worker-Allowed", "value" : "/" }, { "key" : "Cache-Control", "value" : "no-cache" } ] } ] } }
The
public
field tells Firebase that thebuild
folder should be statically served through its CDN. Therewrites
field tells Firebase to send each request to theapp
function - we will create it later. Theheaders
field tells Firebase to add theService-Worker-Allowed
header when the user requests aservice-worker.js
file -
Create a
functions
folder. This folder will contain the code executed by Firebase Functions. You will need to create two files in it:package.json
{ "name": "functions", "description": "Cloud Functions for Firebase", "scripts": { "serve": "firebase serve --only functions", "shell": "firebase functions:shell", "start": "npm run shell", "deploy": "firebase deploy --only functions", "logs": "firebase functions:log" }, "dependencies": { "express": "^4.16.4", "firebase-functions": "^2.1.0", "prpl-server": "^1.3.0", "rendertron-middleware": "^0.1.3" }, "private": true }
index.js
This is the main file for our Firebase Functions. First, we create an Express app. Then, we add a rendertron middleware, that will server side render our PWA for crawler bots, and we give to PRPL Server the responsability to answer to any other GET request. Last but not least, we tell Firebase to make our Express app handle each request to theconst functions = require('firebase-functions'); const prpl = require('prpl-server'); const express = require('express'); const rendertron = require('rendertron-middleware'); const app = express(); const rendertronMiddleware = rendertron.makeMiddleware({ proxyUrl: 'https://render-tron.appspot.com/render', injectShadyDom: true, }); app.use((req, res, next) => { req.headers['host'] = '<YOUR HOST URL HERE>'; return rendertronMiddleware(req, res, next); }); app.get('/*', prpl.makeHandler('./build', require('./build/polymer.json'))); exports.app = functions.https.onRequest(app);
app
function.
-
Install the npm dependencies inside the
functions
directory:$ cd functions && npm i
-
Now that the Firebase Functions part is ready, we have to setup our build to make it work correctly with it. Since Firebase Functions cannot
require
files that are not inside thefunctions
directory, we need to build the PWA and make Gulp move the files needed by PRPL Server to thefunctions
directory, while leaving the static assets in thebuild
directory. To do that, we are going to add a new task togulpfile.js
:/** * Builds the Firebase-ready version of the PWA, moving the necessary * files to the functions folder to be used by PRPL Server */ gulp.task('firebase', () => { // These are the files needed by PRPL Server, that are going to be moved to the functions folder const filesToMove = [ 'build/polymer.json', 'build/**/index.html', 'build/**/push-manifest.json' ]; // Delete the build folder inside the functions folder return del('functions/build') .then(() => // Copy the files needed by PRPL Server new Promise((resolve) => gulp .src(filesToMove, { base: '.' }) .pipe(gulp.dest('functions')) .on('end', resolve))) // Delete them from the original build .then(() => del(filesToMove)); });
-
Optional: If you also need to use Firebase Auth, you have to make sure to tell the Service Worker to ignore its reserved namespace. Add the following code to your
sw-precache-config.js
to tell your Service Worker to useindex.html
as a fallback for everything but Firebase reserved namespace:module.exports = { ... navigateFallback: '/index.html', navigateFallbackWhitelist: [ /^\/[^\_]+\/?/ ] };
-
Add a script named
build:firebase
in your project root'spackage.json
:"build:firebase": "polymer build --auto-base-path && gulp firebase"
-
Now that you have everything set up, just run the
build:firebase
script to build the PWA for Firebase:$ npm run build:firebase
-
Finally, deploy your PWA to Firebase:
$ firebase deploy --only functions,hosting
Hi, the URL passed to Rendertron seems to be of the function's URL though (*. cloudfunctions.net)—as opposed to the request URL (hosting)