我想为Firebase创建多个云功能,并从一个项目同时部署它们。我还想将每个函数分成一个单独的文件。目前我可以创建多个函数,如果我将它们都放在index.js中,例如:
exports.foo = functions.database.ref('/foo').onWrite(event => {
...
});
exports.bar = functions.database.ref('/bar').onWrite(event => {
...
});
但是我想将foo和bar放在单独的文件中。我试过这个:
/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的情况下完成此任务?
答案 0 :(得分:95)
啊,Firebase的云端功能正常加载节点模块,因此可以正常工作
结构:
/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) => {
...
};
答案 1 :(得分:57)
@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');
}
答案 2 :(得分:31)
以下是我个人使用打字稿的方式:
/functions
|--src
|--index.ts
|--http-functions.ts
|--main.js
|--db.ts
|--package.json
|--tsconfig.json
让我作为前言,给出两个警告来完成这项工作:
对于第2点,我不确定为什么。 Secundo你应该尊重我的索引配置,main和db 完全(至少要尝试一下)。
index.ts :处理导出。我发现让index.ts处理出口更加清晰。
// main must be before functions
export * from './main';
export * from "./http-functions";
main.ts :处理初始化。
import { config } from 'firebase-functions';
import { initializeApp } from 'firebase-admin';
initializeApp(config().firebase);
export * from "firebase-functions";
db.ts :只需重新导出数据库,使其名称短于database()
import { database } from "firebase-admin";
export const db = database();
<强> HTTP-functions.ts 强>
// de 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);
}
答案 3 :(得分:10)
借助Cloud / Firebase功能现在提供的Node 8 LTS,您可以使用散布运算符执行以下操作:
new RaisedButton(onPressed:(){selectDate(context);},
color: Colors.lightGreen,
textColor: Colors.grey[200],
child: const Text('اختيا تاريخ الرحلة'),
shape: new RoundedRectangleBorder(borderRadius: new BorderRadius.circular(30.0)),
),
"engines": {
"node": "8"
},
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
};
答案 4 :(得分:9)
.
├── /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
src/index.js
- 主要出口export * from './someFuncA.js';
export * from './someFuncB.js';
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>`,
});
src/store.js
- Firebase Firestore客户端import firebase from 'firebase-admin';
import { config } from 'firebase-functions';
firebase.initializeApp(config().firebase);
export default firebase.firestore();
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);
});
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);
});
.babelrc
{
"presets": [["env", { "targets": { "node": "6.11" } }]],
}
firebase.json
{
"functions": {
"source": ".",
"ignore": [
"**/node_modules/**"
]
}
}
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"
}
}
$ yarn install # Install project dependencies
$ yarn test # Run unit tests
$ yarn deploy # Deploy to Firebase
答案 5 :(得分:6)
为了保持简单(但工作),我亲自构建了我的代码。
<强>布局强>
├── /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,
};
适用于任何嵌套级别的目录。只需按照目录中的模式进行操作。
答案 6 :(得分:5)
此格式允许您的入口点查找其他功能文件,并自动导出每个文件中的每个功能。
主要入口点脚本
查找functions文件夹中的所有.js文件,并导出从每个文件导出的每个函数。
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
}
&#13;
从一个文件导出多个功能的示例
✔ 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
&#13;
http可访问端点适当命名为
const your_functions = require('./path_to_your_functions');
for (var i in your_functions) {
exports[i] = your_functions[i];
}
&#13;
一个文件
如果您只有一些其他文件(例如只有一个),您可以使用:
{{1}}&#13;
答案 7 :(得分:5)
为简单起见(但确实可以做到),我亲自设计了这样的代码。
布局
├── /src/
│ ├── index.ts
│ ├── foo.ts
│ ├── bar.ts
| ├── db.ts
└── package.json
foo.ts
import * as functions from 'firebase-functions';
export const fooFunction = functions.database()......... {
//do your function.
}
export const someOtherFunction = functions.database().......... {
// do the thing.
}
bar.ts
import * as functions from 'firebase-functions';
export const barFunction = functions.database()......... {
//do your function.
}
export const anotherFunction = functions.database().......... {
// do the thing.
}
db.ts
import * as admin from 'firebase-admin';
import * as functions from 'firebase-functions';
export const firestore = admin.firestore();
export const realtimeDb = admin.database();
index.ts
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';
适用于任何嵌套级别的目录。只需遵循目录中的模式即可。
@zaidfazil答案的信用额
答案 8 :(得分:2)
从长远来看,有一种很好的方法来组织所有云功能。我最近做了这个,它运行正常。
我所做的是根据触发器的终结点将每个云功能组织在单独的文件夹中。每个云函数文件名都以*.f.js
结尾。例如,如果您在onCreate
上有onUpdate
和user/{userId}/document/{documentId}
触发器,则在目录onCreate.f.js
中创建两个文件onUpdate.f.js
和functions/user/document/
,函数将分别命名为userDocumentOnCreate
和userDocumentOnUpdate
。 (1)
这是示例目录结构:
functions/
|----package.json
|----index.js
/----user/
|-------onCreate.f.js
|-------onWrite.f.js
/-------document/
|------------onCreate.f.js
|------------onUpdate.f.js
/----books/
|-------onCreate.f.js
|-------onUpdate.f.js
|-------onDelete.f.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const db = admin.database();
const documentsOnCreate = functions.database
.ref('user/{userId}/document/{documentId}')
.onCreate((snap, context) => {
// your code goes here
});
exports = module.exports = documentsOnCreate;
const glob = require("glob");
const camelCase = require('camelcase');
const admin = require('firebase-admin');
const serviceAccount = require('./path/to/ServiceAccountKey.json');
try {
admin.initializeApp({ credential: admin.credential.cert(serviceAccount),
databaseURL: "Your database URL" });
} catch (e) {
console.log(e);
}
const files = glob.sync('./**/*.f.js', { cwd: __dirname });
for (let f = 0, fl = files.length; f < fl; f++) {
const file = files[f];
const functionName = camelCase(file.slice(0, -5).split('/'));
if (!process.env.FUNCTION_NAME || process.env.FUNCTION_NAME === functionName) {
exports[functionName] = require(file);
}
}
(1):可以使用任何所需的名称。对我来说,onCreate.f.js,onUpdate.f.js等似乎与它们所使用的触发器更相关。
答案 9 :(得分:2)
Firebase文档现已更新,其中提供了有关多文件代码组织的良好指南:
Docs > Cloud Functions > Write functions > Organize functions
总结:
foo.js
const functions = require('firebase-functions');
exports.foo = functions.https.onRequest((request, response) => {
// ...
});
bar.js
const functions = require('firebase-functions');
exports.bar = functions.https.onRequest((request, response) => {
// ...
});
index.js
const foo = require('./foo');
const bar = require('./bar');
exports.foo = foo.foo;
exports.bar = bar.bar;
答案 10 :(得分:1)
所以我有一个具有后台功能和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/
文件夹用于功能之间的共享代码
在这里,您可以导入所需的所有功能并声明它们。这里不需要逻辑。我认为它可以使它更清洁。
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);
每次将更改推送到存储库时,进行持续的集成和部署怎么样?您可以使用Google google cloud build拥有它。直到特定时间点它都是免费的:)选中此link。
./ 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
答案 11 :(得分:1)
我花了很多时间寻找相同的东西,而我认为这是实现它的最好方法(我使用的是firebase@7.3.0):
https://codeburst.io/organizing-your-firebase-cloud-functions-67dc17b3b0da
没有汗水;)
答案 12 :(得分:1)
我使用香草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文件仅在根目录内自动包含目录。可以将其扩展为遍历目录,使用.gitignore等。这对我来说足够了。
有了索引文件,添加新功能很简单。
/test/testA.js
const functions = require('firebase-functions');
exports.helloWorld = functions.https.onRequest((request, response) => {
response.send("Hello from Firebase!");
});
/test/testB.js
const functions = require('firebase-functions');
exports.helloWorld2 = functions.https.onRequest((request, response) => {
response.send("Hello again, from Firebase!");
});
npm run serve
产生:
λ ~/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文件。
答案 13 :(得分:0)
bigcodenerd.org概述是一种更简单的体系结构模式,用于将方法分离到不同的文件中,并在 index.js 文件中的一行中导出。>
此示例中项目的体系结构如下:
projectDirectory
index.js
const admin = require('firebase-admin');
const podcast = require('./podcast');
const profile = require('./profile');
admin.initializeApp();
exports.getPodcast = podcast.getPodcast();
exports.removeProfile = user.removeProfile();
podcast.js
const functions = require('firebase-functions');
exports.getPodcast = () => functions.https.onCall(async (data, context) => {
...
return { ... }
});
配置文件文件中的removeProfile
方法将使用相同的模式。
答案 14 :(得分:0)
如果要使用打字稿创建云函数,这是一个简单的答案。
/functions
|--index.ts
|--foo.ts
在顶部所有常规导入附近只需导出foo.ts
中的所有功能。
export * from './foo';
答案 15 :(得分:0)
以上答案为我指明了正确的方向,只是没有一个对我真正有用。下面是一个可用的原型,它是onCall,onRequest和数据库触发器的示例
foo.js-onCall
exports.handler = async function(data, context, admin) {
// const database = admin.database();
// const firestore = admin.firestore();
//...
};
bar.js-onRequest
exports.handler = async function(req, res, admin) {
// const database = admin.database();
// const firestore = admin.firestore();
//...
};
jar.js-触发器/文档/ onCreate
exports.handler = async function(snapshot, context, admin) {
// const database = admin.database();
// const firestore = admin.firestore();
//...
};
index.js
//导入firebase admin SDK依赖项
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
// import functions
const foo = require("./foo");
const bar = require("./bar");
const jar = require("./jar");
// onCall for foo.js
exports.foo = functions.https.onCall((data, context) => {
return foo.handler(data, context, admin);
});
// onRequest for bar.js
exports.bar = functions.https.onRequest((req, res) => {
return bar.handler(req, res, admin);
});
// document trigger for jar.js
exports.jar = functions.firestore
.document("parentCollection/{parentCollectionId}")
.onCreate((snapshot, context) => {
return jar.handler(snapshot, context, admin);
});
注意:您还可以创建一个子文件夹来存放您的各个功能
答案 16 :(得分:0)
为了实现@zaidfazil 的解决方案,我想出了以下内容(使用 JavaScript,而不是 TypeScript)。
multi.js
exports.onQuestionMultiCreate = functions.database
.ref("/questions-multi/{questionId}")
.onCreate(async (snapshot, context) => {
...
}
});
trueFalse.js
exports.onQuestionTrueFalseCreate = functions.database
.ref("/questions-truefalse/{questionId}")
.onCreate(async (snapshot, context) => {
...
}
});
index.js
const multi = require("./multi");
const trueFalse = require("./trueFalse");
module.exports = {
...multi,
...trueFalse