Module

@admin-bro/firebase-functions

A plugin that allows you to render AdminBro by Firebase Cloud Functions

Installation

Before you start make sure you have the firebase app set up (https://firebase.google.com/docs/functions/get-started):

yarn global add firebase-tools
firebase login
firebase init functions
cd functions
// you might need to change version of node to 10 in your package.json
yarn add admin-bro @admin-bro/firebase-functions

Usage on emulator

To run AdminBro locally use buildHandler factory method:

const functions = require('firebase-functions')
const { buildHandler } = require('@admin-bro/firebase-functions')

// assume that you kep all you AdminBroOptions in this file
const adminBroOptions = require('./admin/config')

const onRequestHandler = buildHandler(adminBroOptions, {
  region: 'us-central1',
  before: async () => {
    // connect with database here (i.e.)
    // if this function returns something - it will replace
    // adminBroOptions passed as first argument to buildHandler
  },
  auth: {
    secret: 'super-secret-string-which-encrypts-session',
    authenticate: async (email, password) => {
       // find user and check password
       return foundUser
    }
  }
})

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

And this is it - you have the working AdminBro instance which can be checked by running:

yarn serve

Deploy script

AdminBro bundles custom components to ./.adminbro folder. In other plugins (@admin-bro/express, @admin-bro/hapi) this is done on the server-side. On firebase we cannot write files in the project directory so we have to bundle files manually before the deployment.

In order to do this create a simple bundle script in ./bin/bundle.js file:

// ./bin/bundle.js
const AdminBro = require('admin-bro');

// assume that you keep all your AdminBroOptions in this file
const adminBroOptions = require('../admin/config')

const admin = new AdminBro(adminBroOptions);
admin.initialize();

and run this script before the deployment with NODE_ENV set to production.

To simplify it, you can update scripts in your package.json file like this:

{
  "scripts": {
    "bundle": "NODE_ENV=production node bin/bundle",
    "deploy": "yarn bundle && firebase deploy --only functions && rm .adminbro/bundle.js"
  }
}

After a successful deployment, we can remove the generated bundle.

Now simply run:

yarn deploy

and in a minute you will see your app on Google Cloud Functions for Firebase

Do this even better.

AdminBro serves 4 major assets:

  • global.bundle.js which contains react, redux, axios etc.
  • design-system.bundle.js with AdminBro Design System
  • app.bundle.js where the entire AdminBro frontend application resides
  • components.bundle.js - this is the place for bundled (with AdminBro.bundle) custom components (admin.initialize(); creates it in ./adminbro/bundle.js)

And 2 less important: logo and favicon which can be changed in the AdminBroOptions.

So it means that your function will have to serve these all assets every time the user opens the page with a web browser - meaning more function calls and cold start problems.

You can change that by setting AdminBroOptions.assetsCDN to bypass serving assets right from AdminBro.

Before the deployment you can copy those files to the /public directory and host this directory via firebase hosting. Next point AdminBroOptions.assetsCDN to the hosting URL and you will save these extra calls to your function.

First, you will need to add firebase hosting to your app and set it up to host files from ./public directory.

Next, we have to update ./bin/bundle.js to copy assets to the /public folder:

const AdminBro = require('admin-bro');

// assume that you keep all your AdminBroOptions in this file
const adminBroOptions = require('../admin/config')

const admin = new AdminBro(adminBroOptions);
fs.copyFile(
  './node_modules/admin-bro/lib/frontend/assets/scripts/app-bundle.production.js',
  './public/app.bundle.js',
);
fs.copyFile(
  './node_modules/admin-bro/lib/frontend/assets/scripts/global-bundle.production.js',
  './public/global.bundle.js',
);
fs.copyFile(
  './node_modules/@admin-bro/design-system/bundle.production.js',
  './public/design-system.bundle.js',
);

// this is optional - or simply change default logo
fs.copyFile(
  './node_modules/admin-bro/lib/frontend/assets/images/logo.svg',
  './public/logo.svg',
)
admin.initialize().then(() => {
  fs.rename('./.adminbro/bundle.js', './public/components.bundle.js');
})

This script doesn't create a folder so you have to mkdir it manually.

Finally updated deploy script:

"deploy": "yarn bundle && firebase deploy --only functions,hosting"

Also, you will have to update your firebase.json with information about the hosting page.

Custom domain

So let's assume that you have a rootUrl set to / in AdminBro. Your function target name, (how you name your export) is app.

So the root of the page will be: YOUR-FUNCTION-HOST/app/.

Depending on the environment, (emulator or an actual firebase domain) @admin-bro/firebase-functions will properly adjust the path, that AdminBro knows where to redirect users (not to / but to /app).

In such a case, you don't need to do anything,

But now you are adding a reverse prox, which redirects traffic from your-domain.com/app to YOUR-FUNCTION-HOST/app.

And now admin does not know how to build the URL because he thinks that, requests are not namespaces (not from the firebase domain).

So we have to tell AdminBro that, to the rootUrl he has to prepend the customFunctionPath.

CustomPropertyPath should be app because path going from firebase to admin will be app/ but admin is waiting for / (rootUrl).

customPropertyPath is a member of BuildHandlerOptions

View Source admin-bro-firebase-functions/index.js, line 1

Methods

# buildHandler(adminOptions, options) → {BuildHandlerReturn}

Builds the handler which can be passed to firebase functions

usage:

const functions = require('firebase-functions')
const { buildHandler } = require('@admin-bro/firebase-functions')

const adminOptions = {...}
const region = '...'

exports.app = functions.https.onRequest(buildHandler(adminOptions, { region }));

Parameters:
Name Type Description
adminOptions AdminBroOptions

options which are used to initialize AdminBro instance

options BuildHandlerOptions

custom options for @admin-bro/firebase-functions adapter

View Source admin-bro-firebase-functions/src/plugin.ts, line 39

function which can be passed to firebase

BuildHandlerReturn

Type Definitions

object

# BuildHandlerOptions

Properties:
Name Type Attributes Description
region string

Region where function is deployed

before function <optional>

Optional before async hook which can be used to initialize database., if it returns something it will be used as AdminBroOptions.

auth object <optional>

custom authentication option. If given AdminBro will render login page

secret string

secret which is used to encrypt the session cookie

authenticate function

authenticate function

maxAge number <optional>

For how long cookie session will be stored., Default to 900000 (15 minutes)., In milliseconds.

customFunctionPath string <optional>

Adjustment path when you proxy the domain. Use case: you proxy your-domain.com/app to admin, firebase function with admin having rootUrl=='/' then you have to tell admin that all paths, he receives are /app namespaced so he can properly resolve them. In such case, customFunctionPath should be set to app because proxy path - rootUrl === 'app'.

View Source admin-bro-firebase-functions/src/utils/build-handler-options.ts, line 7

SoftwareBrothers

Proudly built and maintained by SoftwareBrothers

Software House with a passion for both JavaScript and TypeScript.

See what we do See what we believe in

Proudly built and maintained by

SoftwareBrothers