ฉันจะจัดโครงสร้างฟังก์ชั่นคลาวด์สำหรับ Firebase เพื่อปรับใช้หลายฟังก์ชั่นจากหลายไฟล์ได้อย่างไร


164

ฉันต้องการสร้างฟังก์ชั่นคลาวด์หลายอย่างสำหรับ 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 หรือไม่?


1
@JPVentura ไม่เข้าใจคุณจริงๆ กรุณาอธิบาย.
HuyLe

มีการอัปเดตสำหรับ v1.0 นี้หรือไม่ ฉันมีปัญหา: stackoverflow.com/questions/50089807/…
tccpg288

2
FYI ตัวอย่างฟังก์ชัน Firebase อย่างเป็นทางการนี้มีหลาย.jsไฟล์ที่นำเข้าผ่านrequire: github.com/firebase/functions-samples/tree/master/…
xanderiel

สิ่งนี้อาจมีประโยชน์: stackoverflow.com/questions/43486278/…
Ramesh-X

คำตอบ:


126

ฟังก์ชั่นคลาวด์สำหรับโมดูลโหนดโหลด 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
ยกตัวอย่างเช่นฉันสามารถมีฟังก์ชั่นหลายอย่างในโมดูล foo ได้หรือไม่? ถ้าเป็นเช่นนั้นจะดีกว่าที่จะใช้มันได้อย่างไร
Alexander Khitev

1
ฉันคิดว่าคุณสามารถทำได้และกำหนดตัวจัดการที่แตกต่างกันไปยังฟังก์ชั่นการส่งออกที่แตกต่างกันจาก foo: exports.bar = functions.database.ref ('/ foo') onWrite (fooModule.barHandler); exports.baz = functions.database.ref ('/ bar'). onWrite (fooModule.bazHandler);
jasonsirota

44
ฉันไม่ชอบวิธีแก้ปัญหานี้เพราะมันย้ายข้อมูล (เช่นเส้นทางของฐานข้อมูล) จาก foo.js และ bar.js ไปยัง index.js ซึ่งประเภทใดที่เอาชนะจุดที่มีไฟล์แยกเหล่านั้น
bvs

ฉันเห็นด้วยกับ @bvs ฉันคิดว่า Ced มีวิธีการที่ดี ฉันจะแก้ไขเล็กน้อยโดยส่งออกแต่ละโมดูลอย่างชัดเจนเพื่อให้ index.ts super clear เช่นส่งออก {newUser} จาก "./authenticationFunctions"
Alan

2
ฉันคิดว่าคำถามดั้งเดิมของฉันเกี่ยวกับการปรับใช้หลายฟังก์ชั่นกับ 1 โครงการโดยไม่ต้องใส่ฟังก์ชั่นในไฟล์ index.js ที่ไหนและอย่างไรที่คุณส่งข้อมูลฐานข้อมูลไม่อยู่ในขอบเขต ถ้าเป็นฉันฉันอาจจะสร้างโมดูลแยกต่างหากที่ควบคุมการเข้าถึงฐานข้อมูลและต้องการมันใน foo.js และ bar.js แยกกัน แต่นั่นเป็นการตัดสินใจที่มีสไตล์
jasonsirota

75

คำตอบโดย @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'); 
}

โครงสร้างปัจจุบันใน index.js ทำงานได้ไม่ดีสำหรับฉัน สิ่งที่ฉันต้องทำคือการนำเข้าโมดูล firebase ก่อนจากนั้นเริ่มต้นแอพแล้วนำเข้าฟังก์ชั่นจากโฟลเดอร์อื่น ด้วยวิธีนี้แอพของฉันจะเริ่มต้นตรวจสอบความถูกต้องอะไรก็ตามจากนั้นนำเข้าฟังก์ชั่นที่จำเป็นต้องมีแอพที่จะเริ่มต้นได้ก่อน
tonkatata

47

อัปเดต: เอกสารนี้ควรช่วยได้คำตอบของฉันเก่ากว่าเอกสารนี้


นี่คือวิธีที่ฉันทำด้วยตัวพิมพ์เองโดยส่วนตัว:

/functions
   |--src
      |--index.ts
      |--http-functions.ts
      |--main.js
      |--db.ts
   |--package.json
   |--tsconfig.json

ให้ฉันนำหน้านี้โดยให้สองคำเตือนเพื่อให้งานนี้:

  1. คำสั่งของการนำเข้า / ส่งออกมีความสำคัญในindex.ts
  2. db ต้องเป็นไฟล์แยกต่างหาก

สำหรับจุดที่ 2 ฉันไม่แน่ใจว่าทำไม Secundo คุณควรเคารพการตั้งค่าของฉันดัชนีหลักและ 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 : เพียงส่งออก db อีกครั้งเพื่อให้ชื่อสั้นกว่า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);
}

tsconfig ของคุณมีลักษณะอย่างไร ฉันจะรวบรวมไฟล์ dist และให้ฟังก์ชั่น gcloud รู้ว่า index.js ของฉันอยู่ที่ไหน คุณมีรหัสของคุณใน GitHub? :)
bersling

@ choopage-JekBao ขอโทษที่ใช้เวลานานมากฉันไม่มีโปรเจ็กต์นี้อีกแล้ว ถ้าฉันจำได้อย่างถูกต้องคุณสามารถให้ firebase กำหนดค่าไดเรกทอรี (ซึ่งเป็นค่าเริ่มต้น) ฉันอาจจะผิด แต่เนื่องจากมันใช้เวลานานกว่าหนึ่งปี
Ced

สวัสดี @ced - เหตุใดเนื้อหาdb.tsภายในจึงไม่สามารถเข้ามาได้main.ts(หลังจากการสร้างอินสแตนซ์ของผู้ดูแลระบบ) หรือคุณแยกทางด้วยวิธีนี้เพื่อความชัดเจน / ความเรียบง่าย?
dsg38

1
@ dsg38 นี้ถูกโพสต์นานเกินไปที่ผ่านมาผมไม่เห็นเหตุผลที่มันควรจะอยู่ในแฟ้มต่างหากมองไปที่คำตอบตอนนี้ .. ฉันคิดว่ามันเป็นความชัดเจน
Ced

21

ด้วย Node 8 LTS ตอนนี้พร้อมใช้งานกับฟังก์ชั่นคลาวด์ / ฐานไฟคุณสามารถทำสิ่งต่อไปนี้กับตัวดำเนินการสเปรด:

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

ฉันสงสัยว่าจำนวนที่เพิ่มขึ้นของการนำเข้าช้าลงทำให้การเริ่มเย็นของแต่ละฟังก์ชั่นหรือว่าควรจะมีโมดูลแยกโดยสิ้นเชิงจำนวนมากที่พัฒนาแยกต่างหาก?
Simon Fakir

2
ฉันพบข้อผิดพลาดunexpected token ...ในการแบ่งส่วน eslint ภายใน index.js
มัส

บางทีคุณอาจไม่ได้ใช้โหนด 8
Luke Pighetti

@SimonFakir เป็นคำถามที่ดี คุณพบบางสิ่งเกี่ยวกับเรื่องนี้หรือไม่?
atereshkov

@atereshkov ใช่ฉันพบวิธีโหลดเฉพาะฟังก์ชันที่ร้องขอรวมถึงการขึ้นต่อกันโดยใช้ "process.env.FUNCTION_NAME" คล้ายกับคำตอบด้านล่าง ฉันยังสามารถแบ่งปัน repo ของฉันเป็นข้อมูลอ้างอิงหากคุณถูก interessted ติดต่อฉัน
Simon Fakir

15

เพื่อให้ง่าย (แต่ใช้งานได้) ฉันได้กำหนดรหัสส่วนตัวของฉันเช่นนี้

แบบ

├── /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 คำตอบ


1
นี่คือหนึ่งในคำตอบที่ง่ายที่สุดสำหรับ Typescript ขอบคุณ คุณจะรับมือกับอินสแตนซ์ฐานข้อมูล firebase เพียงครั้งเดียวได้อย่างไร admin.initializeApp(functions.config().firestore) const db = admin.firestore();คุณใส่สิ่งนี้ไว้ที่ไหนและคุณจะอ้างอิงมันใน foo and bar ได้อย่างไร?
elprl

เฮ้ - ทำไมเนื้อหาไม่สามารถdb.tsเข้าไปข้างในindex.ts(หลังจาก instantiation ของผู้ดูแลระบบ) หรือคุณแยกทางด้วยวิธีนี้เพื่อความชัดเจน / ความเรียบง่าย?
dsg38

@ dsg38 คุณสามารถผสมทั้งหมดเข้าด้วยกันทำให้ชัดเจน
Reza

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


src/index.js - ส่งออกหลัก

export * from './someFuncA.js';
export * from './someFuncB.js';


src/db.js - ไคลเอนต์ Cloud SQL สำหรับ Postgres

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

9

bigcodenerd.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วิธีการในไฟล์โปรไฟล์


7

เพื่อให้ง่าย (แต่ใช้งานได้) ฉันได้กำหนดรหัสส่วนตัวของฉันเช่นนี้

แบบ

├── /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,
};

ใช้งานได้กับไดเรกทอรีทุกระดับที่ซ้อนกัน เพียงทำตามรูปแบบที่อยู่ในไดเรกทอรีด้วย


ฉันไม่เห็นว่าสิ่งนี้จะทำงานได้อย่างไรเนื่องจาก Firebase รองรับโหนด 6.11 ซึ่งไม่สนับสนุนคำสั่งการนำเข้า ES6
Aodh

หากคุณใช้ typescript ปัญหาจะไม่เกิดขึ้น ฉันทำพอร์ตส่วนใหญ่ของรหัสของฉันลงใน typescript เมื่อเร็ว ๆ นี้
zaidfazil

2
ตอนนี้, คุณควรจดสิ่งที่จำเป็นก่อนในคำตอบของคุณ. @Aodh มันใช้งานได้ถ้าคุณใช้ Babel แบบเดียวกับที่ Konstantin ได้อธิบายไว้ในคำตอบ stackoverflow.com/questions/43486278/…
PostureOfLearning

1
ขอบคุณ. สิ่งนี้ใช้ได้กับ typescript และโหนด 6 :)
Ahmad Moussa

4
แทนที่จะนำเข้าและส่งออกซ้ำกับตัวดำเนินการสเปรดคุณไม่สามารถมีexport * from './fooFunctions';และexport * from './barFunctions';ใน index.ts ได้หรือไม่
whatsthatitspat

5

รูปแบบนี้ช่วยให้จุดเริ่มต้นของคุณค้นหาไฟล์ฟังก์ชันเพิ่มเติมและส่งออกแต่ละฟังก์ชันภายในแต่ละไฟล์โดยอัตโนมัติ

สคริปต์จุดเริ่มต้นหลัก

ค้นหาไฟล์. 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];
    }
  }
});

ตัวอย่างการส่งออกหลายฟังก์ชั่นจากไฟล์เดียว

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
}

จุดปลาย 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

หนึ่งไฟล์

หากคุณมีไฟล์เพิ่มเติมเพียงไม่กี่ไฟล์ (เช่นไฟล์เดียว) คุณสามารถใช้:

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

for (var i in your_functions) {
  exports[i] = your_functions[i];
}


สิ่งนี้จะไม่มีโอเวอร์โหลดเมื่อบู๊ตสำหรับทุกฟังก์ชั่นอินสแตนซ์ที่หมุนขึ้นหรือไม่
Ayyappa

4

ดังนั้นฉันจึงมีโครงการนี้ซึ่งมีฟังก์ชั่นพื้นหลังและฟังก์ชั่น 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 / ซีดี

วิธีการเกี่ยวกับการรวมและการปรับใช้อย่างต่อเนื่องทุกครั้งที่คุณผลักดันการเปลี่ยนแปลงของคุณไปยัง repo คุณสามารถมีได้โดยใช้ Google Google Cloud สร้าง ฟรีจนถึงจุดที่แน่นอน :) ตรวจสอบลิงค์นี้

./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

ฉันได้ส่งออกตามที่คุณพูด แต่การปรับใช้ firebase ตรวจพบสิ่งที่อยู่ในท้ายที่สุด ex: ตามรหัสของคุณจะใช้เวลาเพียง module.exports.stripe = function.https.onRequest (แถบ);
OK200

@ OK200 คำสั่งที่คุณใช้กับบรรทัดคำสั่ง firebase คืออะไร? เพื่อช่วยคุณฉันจะต้องเห็นรหัส
ajorquera

3

มีวิธีที่ดีในการจัดระเบียบฟังก์ชั่นคลาวด์ทั้งหมดของคุณในระยะยาว ฉันทำสิ่งนี้เมื่อไม่นานมานี้และมันทำงานได้อย่างไร้ที่ติ

สิ่งที่ฉันทำคือจัดระเบียบแต่ละฟังก์ชั่นคลาวด์ในโฟลเดอร์แยกกันตามจุดสิ้นสุดของทริกเกอร์ *.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;

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

(1): คุณสามารถใช้ชื่อใดก็ได้ที่คุณต้องการ สำหรับฉัน onCreate.f.js, onUpdate.f.js ฯลฯ ดูเหมือนจะเกี่ยวข้องกับชนิดของทริกเกอร์


1
วิธีนี้ดีมาก ฉันสงสัยว่าเป็นไปได้หรือไม่ที่จะปรับเป็นอนุญาตให้เครื่องหมายสแลชในชื่อฟังก์ชันเพื่อให้คุณสามารถแยกเวอร์ชัน API ต่าง ๆ เช่น (api v1, api v2, ฯลฯ )
Alex Sorokoletov

ทำไมคุณต้องการเก็บคลาวด์เวอร์ชันต่าง ๆ ไว้ในโครงการเดียวกัน แม้ว่าคุณจะสามารถทำได้โดยการเปลี่ยนแปลงเล็กน้อยโครงสร้างไดเรกทอรีโดย index.js เริ่มต้นจะปรับใช้ระบบคลาวด์ทุกฟังก์ชั่จนกว่าคุณจะปรับใช้การคัดเลือกหรือการใช้งานถ้าเงื่อนไขใน index.js ของคุณที่ในที่สุดก็จะจบลงแน่นเกินไปรหัสของคุณ
krhitesh

1
ฉันสบายดีกับการปรับใช้ทุกอย่างฉันแค่ต้องการฟังก์ชั่นที่ฉันใส่ (ตัวกระตุ้น http)
Alex Sorokoletov

ฉันคาดหวังว่าทริกเกอร์ http ทุกตัวจะอยู่ใน*.f.jsไฟล์ของตัวเอง อย่างน้อยคุณสามารถทำได้คือการเปลี่ยนชื่อไฟล์สำหรับทุกรุ่นโดย prepending ต่อท้ายเพื่อให้สิ่งที่ต้องการ*.v1.f.jsหรือ*.v2.f.jsฯลฯ (สมมติว่ารุ่นของคุณทั้งหมดของทั้งหมดของทริกเกอร์ http ของคุณจะมีชีวิตอยู่) โปรดแจ้งให้เราทราบหากคุณมีทางออกที่ดีกว่า
krhitesh

1

ฉันใช้ vanload JS bootloader เพื่อรวมฟังก์ชั่นทั้งหมดที่ฉันต้องการใช้โดยอัตโนมัติ

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

index.js (bootloader)

/**
 * 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 ตัวอย่างนี้รวมไดเร็กทอรีโดยอัตโนมัติเท่านั้นภายในรูท มันสามารถขยายได้เพื่อเดินไดเรกทอรีให้เกียรติ. gignignore ฯลฯ มันก็เพียงพอแล้วสำหรับฉัน

เมื่อมีไฟล์ดัชนีอยู่การเพิ่มฟังก์ชั่นใหม่จึงเป็นเรื่องเล็กน้อย

/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 ทุกครั้งที่มีการเพิ่ม / แก้ไข / ลบฟังก์ชั่น / ไฟล์ใหม่


สิ่งนี้จะไม่เกิดขึ้นตอนเย็นหรือ
Ayyappa

1

นี่คือคำตอบง่ายๆหากคุณกำลังสร้างฟังก์ชั่นคลาวด์ด้วย typescript

/functions
|--index.ts
|--foo.ts

foo.tsใกล้การนำเข้าปกติของคุณทั้งหมดที่ด้านบนเป็นเพียงแค่การส่งออกฟังก์ชั่นทั้งหมดจาก

export * from './foo';


0

ฉันใช้เวลามากมายในการมองหาสิ่งเดียวกันและมีสิ่งที่ฉันคิดว่าเป็นวิธีที่ดีที่สุดในการบรรลุเป้าหมาย (ฉันใช้ firebase@7.3.0):

https://codeburst.io/organizing-your-firebase-cloud-functions-67dc17b3b0da

ไม่มีเหงื่อ ;)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.