如何为Firebase构建云功能以从多个文件部署多个功能?

jas*_*ota 135 javascript firebase google-cloud-functions

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

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

exports.bar = functions.database.ref('/bar').onWrite(event => {
    ...
});
Run Code Online (Sandbox Code Playgroud)

但是我想将foo和bar放在单独的文件中.我试过这个:

/functions
|--index.js (blank)
|--foo.js
|--bar.js
|--package.json
Run Code Online (Sandbox Code Playgroud)

foo.js在哪里

exports.foo = functions.database.ref('/foo').onWrite(event => {
    ...
});
Run Code Online (Sandbox Code Playgroud)

和bar.js是

exports.bar = functions.database.ref('/bar').onWrite(event => {
    ...
});
Run Code Online (Sandbox Code Playgroud)

有没有办法在不将所有函数放在index.js中的情况下完成此操作?

jas*_*ota 111

啊,Firebase的云功能正常加载节点模块,所以这样可行

结构体:

/functions
|--index.js
|--foo.js
|--bar.js
|--package.json
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

foo.js:

exports.handler = (event) => {
    ...
};
Run Code Online (Sandbox Code Playgroud)

bar.js:

exports.handler = (event) => {
    ...
};
Run Code Online (Sandbox Code Playgroud)

  • 我不喜欢这个解决方案,因为它将信息(即数据库路径)从foo.js和bar.js移动到index.js中,这种方式会破坏拥有这些单独文件的点. (37认同)
  • 我认为我最初的问题只是关于使用1个项目部署多个功能而不将这些功能放在index.js文件中,在何处以及如何传递数据库信息不在范围之内。如果是我,我可能会创建一个单独的模块来控制数据库访问,并分别在foo.js和bar.js中要求它,但这是一个风格决定。 (2认同)

Col*_*ent 66

@jasonsirota的答案非常有帮助.但是查看更详细的代码可能很有用,尤其是在HTTP触发函数的情况下.

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

目录结构:

    /functions
       |--index.js
       |--foo.js
       |--bar.js
       |--package.json`
Run Code Online (Sandbox Code Playgroud)

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);
});
Run Code Online (Sandbox Code Playgroud)

foo.js:

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

bar.js:

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


Ced*_*Ced 37

这是我个人用打字稿做的人:

/functions
   |--src
      |--index.ts
      |--http-functions.ts
      |--main.js
      |--db.ts
   |--package.json
   |--tsconfig.json
Run Code Online (Sandbox Code Playgroud)

让我作为前言,给出两个警告来完成这项工作:

  1. index.ts中的进口/出口顺序
  2. db必须是一个单独的文件

对于第2点,我不确定为什么.Secundo你应该尊重我的指标,主要和DB配置完全相同(至少尝试一下).

index.ts:处理出口.我发现让index.ts处理出口更清洁.

// main must be before functions
export * from './main';
export * from "./http-functions";
Run Code Online (Sandbox Code Playgroud)

main.ts:处理初始化.

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

initializeApp(config().firebase);
export * from "firebase-functions";
Run Code Online (Sandbox Code Playgroud)

db.ts:只是重新导出数据库,因此其名称短于database()

import { database } from "firebase-admin";

export const db = database();
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)


Luk*_*tti 17

使用Node 8 LTS现在可以使用Cloud/Firebase功能,您可以使用扩展运算符执行以下操作:

/package.json

"engines": {
  "node": "8"
},
Run Code Online (Sandbox Code Playgroud)

/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
};
Run Code Online (Sandbox Code Playgroud)

/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);
  });
Run Code Online (Sandbox Code Playgroud)

  • 我在 index.js 中收到 eslint 分割错误 `unexpected token ...`。 (3认同)
  • 我想知道越来越多的导入是否会减慢每个功能的冷启动速度,或者是否应该单独开发许多完全分离的模块? (2认同)

Ada*_*itz 14

bigcodenerd.org轮廓的,以便有方法更简单的架构模式分成不同的文件,并在出口一线的内index.js文件。

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

项目目录

  • 索引.js
  • 播客.js
  • 配置文件.js

索引.js

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

exports.getPodcast = podcast.getPodcast();
exports.removeProfile = profile.removeProfile();
Run Code Online (Sandbox Code Playgroud)

播客.js

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

exports.getPodcast = () => functions.https.onCall(async (data, context) => {
      ...
      return { ... }
  });
Run Code Online (Sandbox Code Playgroud)

配置文件中的removeProfile方法将使用相同的模式。


Kon*_*kus 10

如果使用Babel/Flow,它将如下所示:

目录布局

.
??? /build/                     # Compiled output for Node.js 6.x
??? /src/                       # Application source files
?   ??? db.js                   # Cloud SQL client for Postgres
?   ??? index.js                # Main export(s)
?   ??? someFuncA.js            # Function A
?   ??? someFuncA.test.js       # Function A unit tests
?   ??? someFuncB.js            # Function B
?   ??? someFuncB.test.js       # Function B unit tests
?   ??? store.js                # Firebase Firestore client
??? .babelrc                    # Babel configuration
??? firebase.json               # Firebase configuration
??? package.json                # List of project dependencies and NPM scripts
Run Code Online (Sandbox Code Playgroud)


src/index.js - 主要出口

export * from './someFuncA.js';
export * from './someFuncB.js';
Run Code Online (Sandbox Code Playgroud)


src/db.js - Postgres的Cloud SQL Client

import { Pool } from 'pg';
import { config } from 'firebase-functions';

export default new Pool({
  max: 1,
  user: '<username>',
  database: '<database>',
  password: config().db.password,
  host: `/cloudsql/${process.env.GCP_PROJECT}:<region>:<instance>`,
});
Run Code Online (Sandbox Code Playgroud)


src/store.js - Firebase Firestore客户端

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

firebase.initializeApp(config().firebase);

export default firebase.firestore();
Run Code Online (Sandbox Code Playgroud)


src/someFuncA.js - 功能A.

import { https } from 'firebase-functions';
import db from './db';

export const someFuncA = https.onRequest(async (req, res) => {
  const { rows: regions } = await db.query(`
    SELECT * FROM regions WHERE country_code = $1
  `, ['US']);
  res.send(regions);
});
Run Code Online (Sandbox Code Playgroud)


src/someFuncB.js - 功能B.

import { https } from 'firebase-functions';
import store from './store';

export const someFuncB = https.onRequest(async (req, res) => {
  const { docs: regions } = await store
    .collection('regions')
    .where('countryCode', '==', 'US')
    .get();
  res.send(regions);
});
Run Code Online (Sandbox Code Playgroud)


.babelrc

{
  "presets": [["env", { "targets": { "node": "6.11" } }]],
}
Run Code Online (Sandbox Code Playgroud)


firebase.json

{
  "functions": {
    "source": ".",
    "ignore": [
      "**/node_modules/**"
    ]
  }
}
Run Code Online (Sandbox Code Playgroud)


package.json

{
  "name": "functions",
  "verson": "0.0.0",
  "private": true,
  "main": "build/index.js",
  "dependencies": {
    "firebase-admin": "^5.9.0",
    "firebase-functions": "^0.8.1",
    "pg": "^7.4.1"
  },
  "devDependencies": {
    "babel-cli": "^6.26.0",
    "babel-core": "^6.26.0",
    "babel-jest": "^22.2.2",
    "babel-preset-env": "^1.6.1",
    "jest": "^22.2.2"
  },
  "scripts": {
    "test": "jest --env=node",
    "predeploy": "rm -rf ./build && babel --out-dir ./build src",
    "deploy": "firebase deploy --only functions"
  }
}
Run Code Online (Sandbox Code Playgroud)


$ yarn install                  # Install project dependencies
$ yarn test                     # Run unit tests
$ yarn deploy                   # Deploy to Firebase
Run Code Online (Sandbox Code Playgroud)


Rez*_*eza 9

为简单起见(但确实可行),我亲自设计了这样的代码。

布局

??? /src/                      
?   ??? index.ts               
?   ??? foo.ts           
?   ??? bar.ts
|   ??? db.ts           
??? package.json  
Run Code Online (Sandbox Code Playgroud)

foo.ts

import * as functions from 'firebase-functions';
export const fooFunction = functions.database()......... {
    //do your function.
}

export const someOtherFunction = functions.database().......... {
    // do the thing.
}
Run Code Online (Sandbox Code Playgroud)

巴特

import * as functions from 'firebase-functions';
export const barFunction = functions.database()......... {
    //do your function.
}

export const anotherFunction = functions.database().......... {
    // do the thing.
}
Run Code Online (Sandbox Code Playgroud)

数据库

import * as admin from 'firebase-admin';
import * as functions from 'firebase-functions';

export const firestore = admin.firestore();
export const realtimeDb = admin.database();
Run Code Online (Sandbox Code Playgroud)

索引

import * as admin from 'firebase-admin';
import * as functions from 'firebase-functions';

admin.initializeApp(functions.config().firebase);
// above codes only needed if you use firebase admin

export * from './foo';
export * from './bar';
Run Code Online (Sandbox Code Playgroud)

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

归功于@zaidfazil答案

  • 这是 Typescript 最简单的答案之一,谢谢。例如,您如何处理 Firebase 数据库的单个实例?`admin.initializeApp(functions.config().firestore) const db = admin.firestore();` 你把它放在哪里以及如何在 foo 和 bar 中引用它? (2认同)

zai*_*zil 7

为了保持简单(但是工作),我亲自构建了我的代码.

布局

??? /src/                      
?   ??? index.ts               
?   ??? foo.ts           
?   ??? bar.ts           
??? package.json  
Run Code Online (Sandbox Code Playgroud)

foo.ts

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

export const someOtherFunction = functions.database().......... {
    // do the thing.
}
Run Code Online (Sandbox Code Playgroud)

bar.ts

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

export const anotherFunction = functions.database().......... {
    // do the thing.
}
Run Code Online (Sandbox Code Playgroud)

index.ts

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

module.exports = {
    ...fooFunctions,
    ...barFunctions,
};
Run Code Online (Sandbox Code Playgroud)

适用于任何嵌套级别的目录.只需遵循目录中的模式.

  • 你不是只能通过扩展运算符导入和重新导出,而是只能从`./fooFunctions'中导出`导出*;`和`./barFunctions'中的`export*';在index.ts中```` (4认同)
  • zaidfazil,您应该记下答案中的任何先决条件.@Aodh,如果你像康斯坦丁在答案中概述的那样使用Babel,它就有效./sf/ask/3044039491/#48825037 (2认同)

Mat*_*out 5

这种格式使您的入口点可以查找其他功能文件,并自动导出每个文件中的每个功能。

主入口点脚本

在functions文件夹中查找所有.js文件,并导出从每个文件导出的每个函数。

const fs = require('fs');
const path = require('path');

// Folder where all your individual Cloud Functions files are located.
const FUNCTIONS_FOLDER = './scFunctions';

fs.readdirSync(path.resolve(__dirname, FUNCTIONS_FOLDER)).forEach(file => { // list files in the folder.
  if(file.endsWith('.js')) {
    const fileBaseName = file.slice(0, -3); // Remove the '.js' extension
    const thisFunction = require(`${FUNCTIONS_FOLDER}/${fileBaseName}`);
    for(var i in thisFunction) {
        exports[i] = thisFunction[i];
    }
  }
});
Run Code Online (Sandbox Code Playgroud)

从一个文件导出多个功能的示例

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

const query = functions.https.onRequest((req, res) => {
    let query = req.query.q;

    res.send({
        "You Searched For": query
    });
});

const searchTest = functions.https.onRequest((req, res) => {
    res.send({
        "searchTest": "Hi There!"
    });
});

module.exports = {
    query,
    searchTest
}
Run Code Online (Sandbox Code Playgroud)

http可访问的端点已适当命名

? functions: query: http://localhost:5001/PROJECT-NAME/us-central1/query
? functions: helloWorlds: http://localhost:5001/PROJECT-NAME/us-central1/helloWorlds
? functions: searchTest: http://localhost:5001/PROJECT-NAME/us-central1/searchTest
Run Code Online (Sandbox Code Playgroud)

一个文件

如果只有几个其他文件(例如,一个),则可以使用:

const your_functions = require('./path_to_your_functions');

for (var i in your_functions) {
  exports[i] = your_functions[i];
}
Run Code Online (Sandbox Code Playgroud)


ajo*_*era 5

所以我有这个具有后台功能和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
Run Code Online (Sandbox Code Playgroud)

注意: 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);
Run Code Online (Sandbox Code Playgroud)

持续集成/持续交付

每次将更改推送到存储库时,如何进行持续集成和部署?您可以使用 google google cloud build来获得它。它是免费的,直到某一点:) 检查此链接

./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
Run Code Online (Sandbox Code Playgroud)


nhe*_*nhe 5

Firebase 文档现已更新,其中包含多文件代码组织的良好指南:

文档 > 云函数 > 编写函数 > 组织函数

总结一下:

foo.js

const functions = require('firebase-functions');
exports.foo = functions.https.onRequest((request, response) => {
  // ...
});
Run Code Online (Sandbox Code Playgroud)

bar.js

const functions = require('firebase-functions');
exports.bar = functions.https.onRequest((request, response) => {
  // ...
});
Run Code Online (Sandbox Code Playgroud)

索引.js

const foo = require('./foo');
const bar = require('./bar');
exports.foo = foo.foo;
exports.bar = bar.bar;
Run Code Online (Sandbox Code Playgroud)