我想为Firebase创建多个云功能,并从一个项目同时部署它们。我还想将每个函数分离到一个单独的文件中。目前,我可以创建多个函数,如果我把它们都放在index.js,如:

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

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

然而,我想把foo和酒吧在单独的文件。我试了一下:

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

foo.js在哪里

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

bar.js是

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

有没有一种方法可以在不把所有函数都放在index.js中的情况下实现这一点?


当前回答

@jasonsirota的回答很有帮助。但是查看更详细的代码可能会有用,特别是在HTTP触发函数的情况下。

使用与@jasonsirota回答中相同的结构,假设你希望在两个不同的文件中有两个单独的HTTP触发函数:

目录结构:

    /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'); 
}

其他回答

我有这个项目,它有后台函数和http函数。我还有用于单元测试的测试。CI/CD将使您在部署云功能时更加轻松

文件夹结构

|-- package.json
|-- cloudbuild.yaml
|-- functions
    |-- index.js
    |-- background
    |   |-- onCreate
    |       |-- index.js
            |-- create.js
    |
    |-- http
    |   |-- stripe
    |       |-- index.js
    |       |-- payment.js
    |-- utils
        |-- firebaseHelpers.js
    |-- test
        |-- ...
    |-- package.json

注意:utils/文件夹用于在函数之间共享代码

函数/ index.js

在这里,您可以导入所需的所有函数并声明它们。这里不需要逻辑。在我看来,这样更干净。

require('module-alias/register');
const functions = require('firebase-functions');

const onCreate = require('@background/onCreate');
const onDelete = require('@background/onDelete');
const onUpdate = require('@background/onUpdate');

const tours  = require('@http/tours');
const stripe = require('@http/stripe');

const docPath = 'tours/{tourId}';

module.exports.onCreate = functions.firestore.document(docPath).onCreate(onCreate);
module.exports.onDelete = functions.firestore.document(docPath).onDelete(onDelete);
module.exports.onUpdate = functions.firestore.document(docPath).onUpdate(onUpdate);

module.exports.tours  = functions.https.onRequest(tours);
module.exports.stripe = functions.https.onRequest(stripe);

CI / CD

每次将更改推送到回购时都进行持续集成和部署如何?你可以使用谷歌谷歌云构建。它是免费的,直到某个点:)检查这个链接。

。/ cloudbuild.yaml

steps:
  - name: "gcr.io/cloud-builders/npm"
    args: ["run", "install:functions"]
  - name: "gcr.io/cloud-builders/npm"
    args: ["test"]
  - name: "gcr.io/${PROJECT_ID}/firebase"
    args:
      [
        "deploy",
        "--only",
        "functions",
        "-P",
        "${PROJECT_ID}",
        "--token",
        "${_FIREBASE_TOKEN}"
      ]

substitutions:
    _FIREBASE_TOKEN: nothing

Node 8 LTS现在可以与Cloud/Firebase函数一起使用,您可以使用扩展操作符执行以下操作:

/ package.json

"engines": {
  "node": "8"
},

/ index.js

const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();

module.exports = {
  ...require("./lib/foo.js"),
  // ...require("./lib/bar.js") // add as many as you like
};

/lib/foo.js

const functions = require("firebase-functions");
const admin = require("firebase-admin");

exports.fooHandler = functions.database
  .ref("/food/{id}")
  .onCreate((snap, context) => {
    let id = context.params["id"];

    return admin
      .database()
      .ref(`/bar/${id}`)
      .set(true);
  });

我使用一个普通的JS引导加载器来自动包含我想使用的所有函数。

├── /functions
│   ├── /test/
│   │   ├── testA.js
│   │   └── testB.js
│   ├── index.js
│   └── package.json

index.js(引导)

/**
 * The bootloader reads all directories (single level, NOT recursively)
 * to include all known functions.
 */
const functions = require('firebase-functions');
const fs = require('fs')
const path = require('path')

fs.readdirSync(process.cwd()).forEach(location => {
  if (!location.startsWith('.')) {
    location = path.resolve(location)

    if (fs.statSync(location).isDirectory() && path.dirname(location).toLowerCase() !== 'node_modules') {
      fs.readdirSync(location).forEach(filepath => {
        filepath = path.join(location, filepath)

        if (fs.statSync(filepath).isFile() && path.extname(filepath).toLowerCase() === '.js') {
          Object.assign(exports, require(filepath))
        }
      })
    }
  }
})

这个例子index.js文件只在根目录中自动包含目录。它可以扩展到walk目录,honor .gitignore等。不过这对我来说已经足够了。

有了索引文件,添加新函数就很简单了。

/测试/ testA.js

const functions = require('firebase-functions');

exports.helloWorld = functions.https.onRequest((request, response) => {
 response.send("Hello from Firebase!");
});

/测试/ testB.js

const functions = require('firebase-functions');

exports.helloWorld2 = functions.https.onRequest((request, response) => {
 response.send("Hello again, from Firebase!");
});

NPM运行服务产生:

λ ~/Workspace/Ventures/Author.io/Firebase/functions/ npm run serve

> functions@ serve /Users/cbutler/Workspace/Ventures/Author.io/Firebase/functions
> firebase serve --only functions


=== Serving from '/Users/cbutler/Workspace/Ventures/Author.io/Firebase'...

i  functions: Preparing to emulate functions.
Warning: You're using Node.js v9.3.0 but Google Cloud Functions only supports v6.11.5.
✔  functions: helloWorld: http://localhost:5000/authorio-ecorventures/us-central1/helloWorld
✔  functions: helloWorld2: http://localhost:5000/authorio-ecorventures/us-central1/helloWorld2

这个工作流程基本上就是“编写并运行”,而不必在每次添加/修改/删除新函数/文件时修改index.js文件。

为了保持简单(但能完成工作),我个人是这样构造我的代码的。

布局

├── /src/                      
│   ├── index.ts               
│   ├── foo.ts           
│   ├── bar.ts           
└── package.json  

foo.ts

export const fooFunction = functions.database()......... {
    //do your function.
}

export const someOtherFunction = functions.database().......... {
    // do the thing.
}

bar.ts

export const barFunction = functions.database()......... {
    //do your function.
}

export const anotherFunction = functions.database().......... {
    // do the thing.
}

index.ts

import * as fooFunctions from './foo';
import * as barFunctions from './bar';

module.exports = {
    ...fooFunctions,
    ...barFunctions,
};

适用于任何嵌套级别的目录。也只需遵循目录中的模式即可。

org大纲是一个更简单的体系结构模式,可以将方法分离到不同的文件中,并在index.js文件中的一行中导出。

本示例中项目的架构如下:

projectDirectory

index.js podcast.js profile.js

index.js

const admin = require('firebase-admin');
const podcast = require('./podcast');
const profile = require('./profile');
admin.initializeApp();

exports.getPodcast = podcast.getPodcast();
exports.removeProfile = profile.removeProfile();

podcast.js

const functions = require('firebase-functions');

exports.getPodcast = () => functions.https.onCall(async (data, context) => {
      ...
      return { ... }
  });

同样的模式将用于概要文件中的removeProfile方法。