Jak zorganizować Cloud Functions for Firebase, aby wdrożyć wiele funkcji z wielu plików?


164

Chciałbym stworzyć wiele funkcji Cloud Functions dla Firebase i wdrożyć je wszystkie w tym samym czasie z jednego projektu. Chciałbym też oddzielić każdą funkcję w osobnym pliku. Obecnie mogę tworzyć wiele funkcji, jeśli umieszczę je obie w pliku index.js, na przykład:

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

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

Chciałbym jednak umieścić foo i bar w osobnych plikach. Próbowałem tego:

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

gdzie jest foo.js

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

a bar.js jest

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

Czy istnieje sposób na osiągnięcie tego bez umieszczania wszystkich funkcji w pliku index.js?


1
@JPVentura. Naprawdę nie rozumiem cię dobrze. Proszę wytłumacz.
HuyLe,

Czy zostało to zaktualizowane dla wersji 1.0? Mam problemy: stackoverflow.com/questions/50089807/ ...
tccpg288

2
Do Twojej wiadomości, ten oficjalny przykład funkcji Firebase zawiera kilka .jsplików zaimportowanych przez require: github.com/firebase/functions-samples/tree/master/…
xanderiel

Odpowiedzi:


126

Ach, Cloud Functions for Firebase normalnie ładuje moduły węzłów, więc to działa

Struktura:

/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
Czy mogę na przykład mieć kilka funkcji w module foo? Jeśli tak, jak lepiej go wdrożyć?
Alexander Khitev

1
Przypuszczam, że możesz i przypisać różne programy obsługi do różnych wyeksportowanych funkcji z foo: exports.bar = functions.database.ref ('/ foo'). OnWrite (fooModule.barHandler); exports.baz = functions.database.ref ('/ bar'). onWrite (fooModule.bazHandler);
jasonsirota

44
Nie podoba mi się to rozwiązanie, ponieważ przenosi informacje (a mianowicie ścieżki bazy danych) z foo.js i bar.js do index.js, co w pewnym sensie pokonuje sens posiadania tych oddzielnych plików.
bvs

Zgadzam się z @bvs, myślę, że Ced ma dobre podejście. Zamierzam nieco zmodyfikować to, wyraźnie eksportując każdy moduł, aby index.ts był bardzo przejrzysty, np. Export {newUser} from "./authenticationFunctions"
Alan

2
Myślę, że moje pierwotne pytanie dotyczyło po prostu wdrażania wielu funkcji w 1 projekcie bez umieszczania funkcji w pliku index.js, gdzie i jak przekazujesz informacje z bazy danych nie jest w zakresie. Gdybym to był ja, prawdopodobnie stworzyłbym osobny moduł, który kontrolowałby dostęp do bazy danych i wymagałby tego osobno w foo.js i bar.js, ale to decyzja stylistyczna.
jasonsirota

75

Odpowiedź @jasonsirota była bardzo pomocna. Ale może być przydatne wyświetlenie bardziej szczegółowego kodu, szczególnie w przypadku funkcji wyzwalanych przez HTTP.

Używając tej samej struktury, co w odpowiedzi @ jasonsirota, powiedzmy, że chcesz mieć dwie oddzielne funkcje wyzwalające HTTP w dwóch różnych plikach:

struktura katalogów:

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

Obecna struktura w index.js nie wyszła mi dobrze. Musiałem najpierw zaimportować moduły Firebase, następnie zainicjować aplikację, a następnie zaimportować funkcje z innych folderów. W ten sposób moja aplikacja najpierw inicjuje się, uwierzytelnia, cokolwiek, a następnie importuje funkcje, które wymagają wcześniejszej inicjalizacji aplikacji.
tonkatata

47

Aktualizacja: ten dokument powinien pomóc , moja odpowiedź jest starsza niż ten dokument.


Oto, jak osobiście to zrobiłem z maszynopisem:

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

Pozwólcie, że poprzedzę to dwoma ostrzeżeniami, aby to zadziałało:

  1. kolejność importu / eksportu ma znaczenie w index.ts
  2. baza danych musi być oddzielnym plikiem

Jeśli chodzi o punkt numer 2, nie jestem pewien, dlaczego. Secundo, powinieneś dokładnie uszanować moją konfigurację index, main i db (przynajmniej żeby ją wypróbować).

index.ts : zajmuje się eksportem. Uważam, że lepiej jest pozwolić plikowi index.ts na eksport.

// main must be before functions
export * from './main';
export * from "./http-functions";

main.ts : zajmuje się inicjalizacją.

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

initializeApp(config().firebase);
export * from "firebase-functions";

db.ts : po prostu ponownie eksportuję bazę danych, aby jej nazwa była krótsza niż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);
}

jak wygląda twój tsconfig? jak skompilować do folderu dist i powiadomić funkcje gcloud, gdzie jest mój plik index.js? Czy masz swój kod na githubie? :)
bersling

@ choopage-JekBao przepraszam, że minęło dużo czasu, nie mam już projektu. Jeśli dobrze pamiętam, możesz nadać konfiguracji firebase katalog (który domyślnie jest publiczny). Mogę się jednak mylić, ponieważ
minął

Hej @ced - dlaczego zawartość nie może db.tswejść do środka main.ts(po wystąpieniu administratora?). A może właśnie podzieliłeś się w ten sposób dla jasności / prostoty?
dsg38

1
@ dsg38 to zostało opublikowane zbyt dawno temu, naprawdę nie rozumiem, dlaczego powinno to być w osobnym pliku, patrząc teraz na odpowiedź .. Myślę, że to było dla jasności
Ced

21

Dzięki Node 8 LTS, który jest teraz dostępny z Cloud / Firebase Functions, możesz wykonywać następujące czynności za pomocą operatorów rozproszenia:

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

Zastanawiam się, czy rosnąca liczba importów spowalnia zimny start każdej funkcji, czy też powinno być wiele całkowicie oddzielonych modułów opracowanych oddzielnie?
Simon Fakir

2
Otrzymuję błąd dzielenia eslinta unexpected token ...wewnątrz index.js.
thomas

Być może nie używasz węzła 8
Luke Pighetti

@SimonFakir dobre pytanie. Znalazłeś coś na ten temat?
atereshkov

@atereshkov tak Znalazłem sposób, aby załadować tylko żądaną funkcję, w tym jej zależności, za pomocą „process.env.FUNCTION_NAME” podobnie do odpowiedzi poniżej. Mogę również udostępnić moje repozytorium jako referencję, jeśli jesteś zainteresowany, skontaktuj się ze mną.
Simon Fakir,

15

Aby zachować prostotę (ale działa), osobiście ustrukturyzowałem swój kod w ten sposób.

Układ

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

Działa dla katalogów na dowolnych zagnieżdżonych poziomach. Po prostu postępuj zgodnie ze wzorcem wewnątrz katalogów.

kredyt dla @zaidfazil odpowiedz


1
To jedna z najprostszych odpowiedzi na Maszynopis, dzięki. Jak na przykład radzisz sobie z pojedynczą instancją bazy danych Firebase? admin.initializeApp(functions.config().firestore) const db = admin.firestore();Gdzie to umieścisz i jak się do tego odnosisz w foo i bar?
elprl

Hej - dlaczego zawartość nie może db.tswejść do środka index.ts(po wystąpieniu administratora?). A może właśnie podzieliłeś się w ten sposób dla jasności / prostoty?
dsg38

@ dsg38 możesz miksować wszystko razem, to wyjaśnia
Reza

10

W przypadku Babel / Flow wyglądałoby to tak:

Układ katalogów

.
├── /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 - Główne produkty eksportowe

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


src/db.js - Klient Cloud SQL dla 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 - Klient Firebase Firestore

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

firebase.initializeApp(config().firebase);

export default firebase.firestore();


src/someFuncA.js - Funkcja 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 - Funkcja 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

Zarys bigcodenerd.org to prostszy wzorzec architektury, w którym metody są rozdzielane na różne pliki i eksportowane w jednym wierszu w pliku index.js .

Architektura projektu w tym przykładzie jest następująca:

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

Ten sam wzorzec zostałby użyty dla removeProfilemetody w pliku profilu .


7

Aby zachować prostotę (ale działa), osobiście ustrukturyzowałem swój kod w ten sposób.

Układ

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

Działa dla katalogów na dowolnych zagnieżdżonych poziomach. Po prostu postępuj zgodnie ze wzorcem wewnątrz katalogów.


Nie rozumiem, jak to mogłoby działać, ponieważ Firebase obsługuje obecnie Node 6.11, który nie obsługuje dyrektyw importu ES6?
Aodh

Jeśli używasz maszynopisu, problem nigdy nie powinien się pojawić. Ostatnio przeportowałem większość mojego kodu na maszynopis.
zaidfazil

2
zaidfazil, prawdopodobnie powinieneś zanotować wszystkie wymagania wstępne w swojej odpowiedzi. @Aodh, działa, jeśli używasz Babel w taki sam sposób, w jaki Konstantin opisał w odpowiedzi. stackoverflow.com/questions/43486278/…
PostureOfLearning

1
Dziękuję Ci. to działało z maszynopisem i węzłem 6 :)
Ahmad Moussa

4
Zamiast importować i reeksportować za pomocą operatorów spreadów, czy nie mógłbyś po prostu mieć export * from './fooFunctions';i export * from './barFunctions';w index.ts?
whatsthatitspat

5

Ten format umożliwia punktowi wejścia automatyczne wyszukiwanie dodatkowych plików funkcji i eksportowanie każdej funkcji w każdym pliku.

Skrypt głównego punktu wejścia

Znajduje wszystkie pliki .js w folderze funkcji i eksportuje każdą funkcję wyeksportowaną z każdego pliku.

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

Przykład eksportu wielu funkcji z jednego pliku

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
}

Dostępne punkty końcowe http są odpowiednio nazwane

✔ 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

Jeden plik

Jeśli masz tylko kilka dodatkowych plików (np. Tylko jeden), możesz użyć:

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

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


Czy nie spowoduje to przeciążenia podczas rozruchu dla każdej instancji funkcji, która się obraca?
Ayyappa

4

Mam więc ten projekt, który ma funkcje w tle i funkcje http. Mam też testy do testów jednostkowych. CI / CD znacznie ułatwi Ci życie podczas wdrażania funkcji chmury

Struktura folderów

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

Uwaga: utils/ folder służy do udostępniania kodu między funkcjami

functions / index.js

Tutaj możesz po prostu zaimportować wszystkie potrzebne funkcje i zadeklarować je. Nie ma potrzeby mieć tutaj logiki. Moim zdaniem sprawia, że ​​jest czystszy.

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

A co z ciągłą integracją i wdrażaniem za każdym razem, gdy wprowadzasz zmiany do repozytorium? Możesz to mieć, używając google google cloud build . Do pewnego momentu za darmo :) Sprawdź ten 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

wyeksportowałem, jak powiedziałeś, ale wdrożenie Firebase wykrywa ten, który jest na końcu, np. zgodnie z twoim kodem zajmuje tylko module.exports.stripe = functions.https.onRequest (stripe);
OK200

@ OK200 jakiego polecenia używasz w wierszu poleceń Firebase? Aby ci pomóc, będę potrzebował trochę kodu
ajorquera

3

Istnieje całkiem dobry sposób na długoterminową organizację wszystkich funkcji chmury. Zrobiłem to niedawno i działa bez zarzutu.

To, co zrobiłem, to zorganizowanie każdej funkcji chmury w oddzielnych folderach na podstawie ich punktu końcowego wyzwalacza. Każda nazwa pliku funkcji chmury kończy się na *.f.js. Na przykład, jeśli masz włączone onCreatei onUpdatewyzwalacze, user/{userId}/document/{documentId}utwórz dwa pliki onCreate.f.jsi onUpdate.f.jsw katalogu, functions/user/document/a twoja funkcja zostanie odpowiednio nazwana userDocumentOnCreatei userDocumentOnUpdate. (1)

Oto przykładowa struktura katalogu:

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

Przykładowa funkcja

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): Możesz użyć dowolnej nazwy. Dla mnie onCreate.f.js, onUpdate.f.js itp. Wydają się bardziej odpowiednie do rodzaju wyzwalacza, którym są.


1
Takie podejście jest naprawdę fajne. Zastanawiałem się, czy można dostosować, aby zezwolić na ukośniki w nazwach funkcji, aby można było oddzielić różne wersje API, na przykład (api v1, api v2 itp.)
Alex Sorokoletov

Dlaczego miałbyś chcieć trzymać różne wersje funkcji chmury w tym samym projekcie? Chociaż możesz to zrobić, nieznacznie zmieniając strukturę katalogów, domyślnie index.js wdroży wszystkie funkcje chmury, chyba że wdrożysz selektywnie lub użyjesz warunków warunkowych w pliku index.js, które ostatecznie
zaśmiecą

1
Jestem w porządku z wdrażaniem wszystkiego, chcę tylko
wersjonować

Oczekuję, że każdy wyzwalacz http znajduje się we własnym *.f.jspliku. Najmniej można zrobić jest zmiana nazwy pliku dla każdej wersji przez poprzedzenie sufiks aby coś *.v1.f.jslub *.v2.f.jsitd. (Zakładając, że wszystkie wersje wszystkich swoich http spuście są pod napięciem). Daj mi znać, jeśli masz lepsze rozwiązanie.
krhitesh

1

Używam programu ładującego vanilla JS do automatycznego dołączania wszystkich funkcji, których chcę używać.

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

index.js (program ładujący)

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

Ten przykładowy plik index.js zawiera automatycznie tylko katalogi w katalogu głównym. Można go rozszerzyć o chodzenie po katalogach, honorowanie .gitignore, itp. To mi jednak wystarczyło.

Po utworzeniu pliku indeksu dodawanie nowych funkcji jest banalne.

/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 plony:

λ ~/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

Ten przepływ pracy jest po prostu „zapisuj i uruchamiaj”, bez konieczności modyfikowania pliku index.js za każdym razem, gdy nowa funkcja / plik jest dodawany / modyfikowany / usuwany.


czy to nie będzie miało na zimny start?
Ayyappa

1

Oto prosta odpowiedź, jeśli tworzysz funkcje chmury za pomocą maszynopisu.

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

W przypadku prawie wszystkich zwykłych importów u góry wyeksportuj wszystkie funkcje z foo.ts.

export * from './foo';


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.