How do I structure Cloud Functions for Firebase to deploy multiple functions from multiple files?

I would like to create multiple Cloud Functions for Firebase and deploy them all at the same time from one project. I would also like to separate each function into a separate file. Currently I can create multiple functions if I put them both in index.js such as:

exports.foo = functions.database.ref('/foo').onWrite(event => {
    ...
});

exports.bar = functions.database.ref('/bar').onWrite(event => {
    ...
});

However I would like to put foo and bar in separate files. I tried this:

/functions
|--index.js (blank)
|--foo.js
|--bar.js
|--package.json

where foo.js is

exports.foo = functions.database.ref('/foo').onWrite(event => {
    ...
});

and bar.js is

exports.bar = functions.database.ref('/bar').onWrite(event => {
    ...
});

Is there a way to accomplish this without putting all functions in index.js?


Ah, Cloud Functions for Firebase load node modules normally, so this works

structure:

/functions
|--index.js
|--foo.js
|--bar.js
|--package.json

index.js:

const functions = require('firebase-functions');
const fooModule = require('./foo');
const barModule = require('./bar');

exports.foo = functions.database.ref('/foo').onWrite(fooModule.handler);
exports.bar = functions.database.ref('/bar').onWrite(barModule.handler);

foo.js:

exports.handler = (event) => {
    ...
};

bar.js:

exports.handler = (event) => {
    ...
};

The answer by @jasonsirota was very helpful. But it may be useful to see more detailed code, especially in the case of HTTP triggered functions.

Using the same structure as in @jasonsirota's answer, lets say you wish to have two separate HTTP trigger functions in two different files:

directory structure:

    /functions
       |--index.js
       |--foo.js
       |--bar.js
       |--package.json

index.js:

'use strict';
const fooFunction = require('./foo');
const barFunction = require('./bar');

// Note do below initialization tasks in index.js and
// NOT in child functions:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase); 
const database = admin.database();

// Pass database to child functions so they have access to it
exports.fooFunction = functions.https.onRequest((req, res) => {
    fooFunction.handler(req, res, database);
});
exports.barFunction = functions.https.onRequest((req, res) => {
    barFunction.handler(req, res, database);
});

foo.js:

 exports.handler = function(req, res, database) {
      // Use database to declare databaseRefs:
      usersRef = database.ref('users');
          ...
      res.send('foo ran successfully'); 
   }

bar.js:

exports.handler = function(req, res, database) {
  // Use database to declare databaseRefs:
  usersRef = database.ref('users');
      ...
  res.send('bar ran successfully'); 
}

Update: Typescript is now fully supported so no need for the shenanigans below. Just use the firebase cli


Here is how I personnally did it with typescript:

/functions
   |--src
      |--index.ts
      |--http-functions.ts
      |--main.js
      |--db.ts
   |--package.json
   |--tsconfig.json

Let me preface this by giving two warnings to make this work:

  1. the order of import / export matters in index.ts
  2. the db must be a separate file

For point number 2 I'm not sure why. Secundo you should respect my configuration of index, main and db exactly (at least to try it out).

index.ts : deals with export. I find it cleaner to let the index.ts deal with exports.

// main must be before functions
export * from './main';
export * from "./http-functions";

main.ts: Deals with initialization.

import { config } from 'firebase-functions';
import { initializeApp } from 'firebase-admin';

initializeApp(config().firebase);
export * from "firebase-functions";

db.ts: just reexporting the db so its name is shorter than database()

import { database } from "firebase-admin";

export const db = database();

http-functions.ts

// db must be imported like this
import { db } from './db';
// you can now import everything from index. 
import { https } from './index';  
// or (both work)
// import { https } from 'firebase-functions';

export let newComment = https.onRequest(createComment);

export async function createComment(req: any, res: any){
    db.ref('comments').push(req.body.comment);
    res.send(req.body.comment);
}