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:
- the order of import / export matters in index.ts
- 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);
}