npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

quiver-functions

v1.5.0

Published

A monorepo of useful Firebase Cloud Functions

Downloads

21

Readme

Quiver Functions

A collection of little helpers for your Firebase Functions pleasure.

You'll find yourself implenting a lot of the same things over, and over and over. For instance, you'll need to test your functions locally, so you'll be implementing mock events over and over again. And setting functions environment variables programatically is a pain in the neck... so you'll want that automated too.

Installation

  • npm install --save quiver-functions

Testing

  • git clone https://github.com/deltaepsilon/quiver-functions.git && cd quiver-functions
  • Install firebase-tools globally with npm install -g firebase-tools.
  • Run firebase init and connect the local project to one of your Firebase projects. No need to install database, functions or hosting if you're just running tests.
  • Get your CI token with firebase login:ci. Follow the instructions and copy your new CI token from the CLI.
  • Edit ./functions/config.json with your test project's details including the CI token. Let ./functions/config.json.dist be your guide.
  • cd quiver-functions
  • npm install
  • cd ..
  • npm test

Test bed web page

  • npm run install-public
  • npm run serve
  • Edit ./public/env.client.json with your project's details. Let ./public/env.client.json.dist be your guide.
  • Open http://localhost:8080/
  • firebase deploy --only database: Deploys database rules
  • firebase deploy --only functions: Deploys functions
  • firebase deploy --only hosting: Deploys hosting; use if you want to host the test bed web page.
  • Profit

How to use the module

QuiverFunctions export statement looks like this:

module.exports = {
  UpdateUser: require('./functions/onCreate/updateUser.onCreate'),
  Login: require('./functions/onWrite/login.onWrite'),
  Environment: require('./functions/onRequest/environment.onRequest'),
  mocks: require('./mocks/mocks'),
  utilities: require('./utilities/utilities')
}

UpdateUser

UpdateUser is a simple handler for user-creation events. It simply copies the new user's JWT data to whatever collection you specify with the usersPath parameter.

Login

Login is designed so that every time your user logs in, he'll add his token from firebase.auth().currentUser.getToken(token => console.log(token)) queue, and Login will process that queue item, verifying the ID token, checking to see if the user's email is in the adminUsers array and adding the user.isAdmin flag as appropriate. The user details are then updated to usersPath/{uid} based on the specified usersPath param.

Login requires {uid} to be in the ref path. It's a generic solution, I know, but every app I've written in the last year has used this functionality, written exactly like this.

EnvironmentService

EnvironmentService takes environment config and has a utility method called getPublicEnvironment(host) that will return just the .public node of your environment as well as complete any overrides based on the host that you pass in. Note that keys can't have periods in them so we're using _ instead of dots. Here's a quick example:

"public": {
  "a": "defaults: a",
  "models": {
    "queues": {
      "current-user": "quiver-functions/queues/current-user"
    }
  },
  "localhost": {
    "a": "localhost: a",
    "b": "localhost: b"
  },
  "subdomain_domain_tld": {
    "a": "subdomain_domain_tld: a",
    "b": "subdomain_domain_tld: b"
  }
}

public and domain-specific overrides

I always find myself wanting some of my environment variables to be public so that I can quickly pass them to my client apps. I also want to be able to override these public variables based on domain. I use different domains for dev, test and prod, so I should be able to easily override my public environment variables.

Check out ./functions/config.json.dist for a complete example.

Environment Variable Rules

Functions has some rules for environment variables/config.

  • No root-level key can have the word "firebase" in it. There is a firebase: {} attribute that will be filled in automatically by Functions, so you won't have to look far for your core environment variables. Also, it doesn't hurt to keep your own firebase: {} attributes in config.json; just know that this attribute will be deleted upon upload.
  • All config must be nested. You can't do { someValue: 'true' }. It needs to be {some: { value: 'true' }}.
  • Dashes and special characters in attribute names can wreck havoc. So can capitalization. Attribute names should be all lowercase and have no special characters. Underscores are preferred. So don't use {'test-user': {...}}. Use {test_user: {...}} instead.

Environment onRequest Handler

The Environment onRequest handler is a bit fancy. It takes advantage of the fact that firebase-functions looks for environment variables in ./functions/config.json.

First, place all of your environment variables in that .json file, and use ./utilities/environment.utility.js and it's setAll(), unsetAll() and getAll() functions to push those environment variables up to Functions config. Now your local environment and your Functions environment will be equivalent.

Second, export an environment function in your ./functions/index.js. Here's an example:

const Environment = require('quiver-functions').Environment;
const environment = new Environment();
exports.environment = functions.https.onRequest(environment.getFunction());

Third, create a rewrite rule in your ./firebase.json. Here's what I'm using for firebase.json. Notice that the source is /environment, but there's no destination... only a function. That function value matches up to the exports.environment = ... line in the example above.

{
  "database": {
    "rules": "database.rules.json"
  },
  "hosting": {
    "public": "public",
    "rewrites": [
      {
        "source": "/environment.js",
        "function": "environment"
      },
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  }
}

Now that you've completed the redirect, run firebase deploy in your terminal and hit https://your-firebase.firebaseapp.com/environment to see the magic. It's a single script tag that adds your public environment to window.firebaseEnv. This is perfect for importing your client-side environment variables into your dev, test and prod clients.

Full Example

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const config = functions.config();

admin.initializeApp(config.firebase);

const UpdateUser = require('quiver-functions').UpdateUser;
const updateUser = new UpdateUser({
  usersPath: 'quiver-functions/{environment}/users',
  database: admin.database()
});
exports.updateUser = functions.auth.user().onCreate(updateUser.getFunction());

const Login = require('quiver-functions').Login;
const login = new Login({
  usersPath: 'quiver-functions/users',
  adminUsers: ['[email protected]']
});
exports.login = functions.database.ref('quiver-functions/queues/current-user/{uid}').onWrite(login.getFunction());

const config = functions.config();
const headers = {
  'Cache-Control': 'public, max-age=600, s-maxage=600'
};
const Environment = require('quiver-functions').Environment;
const environment = new Environment({ config, headers });
exports.environment = functions.https.onRequest(environment.getFunction());

Utilities

QuiverFunctions.utilities is a collection of useful little helpers. So far it's just utililities.EnvironmentUtility, which manipulates Firebase Functions config as found in ./functions/config.json. There are three functions on EnvironmentUtility:

  1. EnvironmentUtility.getAll()
  2. EnvironmentUtility.unsetAll()
  3. EnvironmentUtility.setAll()

Example

const config = require('../functions/config.json');
const quiverFunctions = require('quiver-functions');
const environmentUtility = new quiverFunctions.utilities.EnvironmentUtility('quiver-two', 'my-ci-token-from-firebase-tools-cli', config);

environmentUtility.unsetAll()
  .then(() => {
    return environmentUtility.setAll();
  })
  .then(() => {
    return environmentUtility.getAll();
  })
  .then(newConfig => {
    console.log('New Firebase Functions config: ', newConfig);
  });

Mocks

QuiverFunctions.mocks is a collection of useful mocks for testing Firebase Functions locally. See the code in /mocks to see how the objects are built. Check out /functions/lib/login.spec.js and /functions/lib/on-create.spec.js for example implementation.

Use new functions.database.DeltaSnapshot(...) to create mock DeltaSnapshots as documented here.

Developing Firebase Functions without a local testing environment is... a mistake. A huge mistake. It's basically impossible to develop effectively with a guess-and-check technique. Every call to firebase deploy --only functions takes at least a minute, and then the functions can take a while to warm up, so solving bugs by uploading new "fixed" functions can be a huge waste of time. Just write the tests. You'll be happier. Promise.

QVF

I found myself wanting to fire off the EnvironmentUtility functions directly from the command line, so I wrote a tiny CLI that's available with the command qvf (for quiver-functions). It has three commands and one flag. Here's the full example:

$ qvf unset
$ qvf get
$ qvf set
$ qvf set --environment some/other/environment.json

Note that qvf set defaults to using $PWD/functions/config.json.