javascript - functions - guardar datos en firebase android
¿Cómo estructuro Cloud Functions for Firebase para implementar múltiples funciones desde múltiples archivos? (12)
Ah, las funciones de nube para los módulos de nodo de carga de Firebase normalmente, así que esto funciona
estructura:
/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) => {
...
};
Me gustaría crear múltiples funciones en la nube para Firebase e implementarlas todas al mismo tiempo desde un proyecto. También me gustaría separar cada función en un archivo separado. Actualmente puedo crear múltiples funciones si las pongo en index.js como:
exports.foo = functions.database.ref(''/foo'').onWrite(event => {
...
});
exports.bar = functions.database.ref(''/bar'').onWrite(event => {
...
});
Sin embargo, me gustaría poner foo y bar en archivos separados. Intenté esto:
/functions
|--index.js (blank)
|--foo.js
|--bar.js
|--package.json
donde foo.js es
exports.foo = functions.database.ref(''/foo'').onWrite(event => {
...
});
y bar.js es
exports.bar = functions.database.ref(''/bar'').onWrite(event => {
...
});
¿Hay alguna manera de lograr esto sin poner todas las funciones en index.js?
Así es como lo hice personalmente con el mecanografiado:
/functions
|--src
|--index.ts
|--http-functions.ts
|--main.js
|--db.ts
|--package.json
|--tsconfig.json
Permítanme presentar esto dando dos advertencias para que esto funcione:
- el orden de importación / exportación importa en index.ts
- el db debe ser un archivo separado
Para el punto número 2 no estoy seguro de por qué. Secundo debes respetar mi configuración de index, main y db exactamente (al menos para probarlo).
index.ts : trata con la exportación. Me parece más limpio dejar que index.ts se ocupe de las exportaciones.
// main must be before functions
export * from ''./main'';
export * from "./http-functions";
main.ts : se ocupa de la inicialización.
import { config } from ''firebase-functions'';
import { initializeApp } from ''firebase-admin'';
initializeApp(config().firebase);
export * from "firebase-functions";
db.ts
: simplemente reexportando el db para que su nombre sea más corto que la
database()
de
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);
}
Con el Nodo 8 LTS ahora disponible con las funciones Cloud / Firebase, puede hacer lo siguiente con los operadores de propagación:
/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);
});
En el caso de Babel / Flow se vería así:
Diseño de directorio
.
├── /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
- Principales exportaciones
export * from ''./someFuncA.js'';
export * from ''./someFuncB.js'';
src/db.js
- Cloud SQL Client para 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
: cliente de Firebase Firestore
import firebase from ''firebase-admin'';
import { config } from ''firebase-functions'';
firebase.initializeApp(config().firebase);
export default firebase.firestore();
src/someFuncA.js
- Función 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
- Función 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
Entonces tengo este proyecto que tiene funciones de fondo y funciones http. También tengo pruebas para pruebas unitarias. CI / CD te facilitará la vida al implementar funciones en la nube
Estructura de carpetas
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
Nota:
utils/
folder es para compartir código entre funciones
funciones / index.js
Aquí puede importar todas las funciones que necesita y declararlas. No es necesario tener lógica aquí. Lo hace más limpio en mi opinión.
const admin = require(''firebase-admin'');
const podcast = require(''./podcast'');
const profile = require(''./profile'');
admin.initializeApp();
exports.getPodcast = podcast.getPodcast();
exports.removeProfile = user.removeProfile();
CI / CD
¿Qué le parece tener una integración e implementación continuas cada vez que envía sus cambios al repositorio? Puedes tenerlo usando google google cloud build . Es gratis hasta cierto punto :) Consulte este link .
./cloudbuild.yaml
const functions = require(''firebase-functions'');
exports.getPodcast = () => functions.https.onCall(async (data, context) => {
...
return { ... }
});
Este formato permite que su punto de entrada encuentre archivos de funciones adicionales y exporte cada función dentro de cada archivo, automáticamente.
Script de punto de entrada principal
Encuentra todos los archivos .js dentro de la carpeta de funciones y exporta cada función exportada desde cada archivo.
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];
}
}
});
Ejemplo de exportación de múltiples funciones desde un archivo
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
}
los puntos finales accesibles http se denominan adecuadamente
✔ 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
Un archivo
Si solo tiene unos pocos archivos adicionales (por ejemplo, solo uno), puede usar:
const your_functions = require(''./path_to_your_functions'');
for (var i in your_functions) {
exports[i] = your_functions[i];
}
Hay una manera bastante buena de organizar todas sus funciones en la nube a largo plazo. Hice esto recientemente y está funcionando perfectamente.
Lo que hice fue organizar cada función de la nube en carpetas separadas según su punto final de activación.
Cada nombre de archivo de función de nube termina con
*.f.js
.
Por ejemplo, si tuvo
onCreate
y
onUpdate
en
user/{userId}/document/{documentId}
, cree dos archivos
onCreate.f.js
y
onUpdate.f.js
en las
functions/user/document/
directorio
functions/user/document/
y su función será llamado
userDocumentOnCreate
y
userDocumentOnUpdate
respectivamente.
(1)
Aquí hay una estructura de directorio de muestra:
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
Función de la muestra
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): puede usar el nombre que desee. Para mí, onCreate.f.js, onUpdate.f.js, etc. parecen más relevantes para el tipo de disparador que son.
La respuesta de @jasonsirota fue muy útil. Pero puede ser útil ver un código más detallado, especialmente en el caso de las funciones activadas por HTTP.
Usando la misma estructura que en la respuesta de @ jasonsirota, digamos que desea tener dos funciones de activación HTTP separadas en dos archivos diferentes:
estructura de directorios:
/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'');
}
Para ser simple (pero hace el trabajo), personalmente he estructurado mi código de esta manera.
Diseño
├── /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'';
Funciona para directorios de cualquier nivel anidado. Simplemente siga el patrón dentro de los directorios también.
crédito a @zaidfazil respuesta
Para ser simple (pero hace el trabajo), personalmente he estructurado mi código de esta manera.
Diseño
├── /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,
};
Funciona para directorios de cualquier nivel anidado. Simplemente siga el patrón dentro de los directorios también.
Utilizo un cargador de arranque JS vanilla para incluir automáticamente todas las funciones que quiero usar.
├── /functions
│ ├── /test/
│ │ ├── testA.js
│ │ └── testB.js
│ ├── index.js
│ └── package.json
index.js (gestor de arranque)
/**
* 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))
}
})
}
}
})
Este archivo index.js de ejemplo solo incluye automáticamente directorios dentro de la raíz. Se podría ampliar para recorrer directorios, honrar .gitignore, etc. Sin embargo, esto fue suficiente para mí.
Con el archivo de índice en su lugar, agregar nuevas funciones es trivial.
/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
rendimientos:
λ ~/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
Este flujo de trabajo es simplemente "escribir y ejecutar", sin tener que modificar el archivo index.js cada vez que se agrega / modifica / elimina una nueva función / archivo.
bigcodenerd.org esquema bigcodenerd.org es un patrón de arquitectura más simple para separar los métodos en diferentes archivos y exportarlos en una línea dentro del archivo index.js .
La arquitectura para el proyecto en este ejemplo es la siguiente:
projectDirectory
- index.js
- podcast.js
- profile.js
index.js
|-- 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
podcast.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);
Se usaría el mismo patrón para el método
removeProfile
en el archivo de
perfil
.