我想为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中的情况下实现这一点?
我有这个项目,它有后台函数和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
有一种很好的方法可以长期组织所有的云功能。我最近就这么做了,效果完美无缺。
我所做的是根据每个云函数的触发端点将它们组织在单独的文件夹中。每个云函数的文件名都以*.f.js结尾。例如,如果你在user/{userId}/document/{documententid}上有onCreate和onUpdate触发器,那么在functions/user/document/目录下创建两个文件onCreate.f.js和onUpdate.f.js,你的函数将分别命名为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;
Index.js
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);
}
}
你可以使用任何你想要的名字。对我来说,onCreate.f.js, onUpdate.f.js等似乎更相关的类型的触发器。
为了保持简单(但能完成工作),我个人是这样构造我的代码的。
布局
├── /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的答案
更新:Typescript现在完全支持,所以不需要下面的恶作剧。只需使用firebase cli
以下是我个人是如何使用typescript的:
/functions
|--src
|--index.ts
|--http-functions.ts
|--main.js
|--db.ts
|--package.json
|--tsconfig.json
在此之前,我先提出两点警告:
进口/出口的顺序在index.ts中很重要
db必须是一个单独的文件
第二点,我不知道为什么。其次,你应该完全尊重我的index, main和db的配置(至少要尝试一下)。
索引。Ts:与出口有关。我觉得让索引更干净。贸易部门负责出口。
// main must be before functions
export * from './main';
export * from "./http-functions";
主要。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
// 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);
}
啊,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) => {
...
};