4

I am developing a firebase cloud function that writes to a firestore database.

During development I want the function to write to a local database. So I've started a firestore emulator. But the data is still written to the actual database.

How can I configure the cloud functions to use the local database?

This is my setup:

import * as functions from 'firebase-functions';
import * as cors from "cors";
import * as admin from "firebase-admin";

const REGION = "europe-west1";
const COLLECTION_CONTACT_FORM = "contact_form";

const serviceAccount = require("../keys/auth-key.json");
admin.initializeApp({
    credential: admin.credential.cert(serviceAccount)
});

const corsMiddleware = cors({origin: true});

export const sendContactForm = functions.region(REGION).https.onRequest((request, response) => corsMiddleware(request, response, async () => {
    let {text} = request.body;
    let result = await admin.firestore().collection(COLLECTION_CONTACT_FORM).add({text});
    response.send((result.id));
}));

This is the console output when starting the emulator:

[1] i  firestore: Serving WebChannel traffic on at http://localhost:8081
[1] i  firestore: Emulator logging to firestore-debug.log
[1] ✔  functions: Emulator started at http://localhost:5000
[1] ✔  firestore: Emulator started at http://localhost:8080
[1] i  functions: Watching "path/functions" for Cloud Functions...
[1] ⚠  functions: Your GOOGLE_APPLICATION_CREDENTIALS environment variable points to path/keys/auth-key.json. Non-emulated services will access production using these credentials. Be careful!
[1] ✔  functions[sendContactForm]: http function initialized (http://localhost:5000/project/europe-west1/sendContactForm).

When triggering the local endpoint, the production database is written to.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
McFarlane
  • 1,777
  • 2
  • 22
  • 39

2 Answers2

2

The firestore admin initializeApp() will correctly handle switching between local emulator and production database depending on where it is running. So if you simply remove the service account credentials it should work properly:

import * as functions from 'firebase-functions';
import * as cors from "cors";
import * as admin from "firebase-admin";

const REGION = "europe-west1";
const COLLECTION_CONTACT_FORM = "contact_form";

admin.initializeApp();
const corsMiddleware = cors({origin: true});

export const sendContactForm = functions.region(REGION).https.onRequest((request, response) => corsMiddleware(request, response, async () => {
    let {text} = request.body;
    let result = await admin.firestore().collection(COLLECTION_CONTACT_FORM).add({text});
    response.send((result.id));
}));

But if for some reason you're trying to write to a firestore database outside of the one that the project is created in, you can use firestore/grpc separately from the firebase classes and then use the environment to either include your service account credentials or location emulator credentials. A local emulator example:

const {Firestore} = require('@google-cloud/firestore');
const {credentials} = require('@grpc/grpc-js');

const db = new Firestore({
  projectId: 'my-project-id',
  servicePath: 'localhost',
  port: 5100,
  sslCreds: credentials.createInsecure(),
  customHeaders: {
    "Authorization": "Bearer owner"
  }
});

await db.collection("mycollection").doc("someid").set({ test: "value" });
explunit
  • 18,967
  • 6
  • 69
  • 94
0

Same answer, but with the docId set dynamically.

exports.makeUppercase = functions.firestore.document('Messages/{docId}').onCreate((snap, context) => {
    const original = snap.data().original;
    functions.logger.log('Uppercasing', context.params.docId, original);
    const uppercase = original.toUpperCase();
    // return snap.ref.set({ uppercase }, { merge: true });
    return admin.firestore().collection('AnotherCollection').doc(context.params.docId).set({ uppercase }, { merge: true });
});

This grabs the docId that was set dynamically and uses it to write to a document with the same name but in a different collection.

Also I left in commented code for writing to the same document in the same collection. Beware that using onUpdate or onWrite instead of onCreate makes an infinite loop as each write triggers the function again!

Thomas David Kehoe
  • 10,040
  • 14
  • 61
  • 100