Amazon Lambda to Firebase

半城伤御伤魂 提交于 2019-11-27 04:00:06

To safely use firebase npm package (version 3.3.0) in AWS Lambda (Nodejs 4.3), Please do the following:

'use strict';

var firebase = require("firebase");

exports.handler = (event, context, callback) => {
    context.callbackWaitsForEmptyEventLoop = false;  //<---Important

    var config = {
        apiKey: "<<apikey>>",
        authDomain: "<<app_id>>.firebaseapp.com",
        databaseURL: "https://<<app_id>>.firebaseio.com",
        storageBucket: "<<app_id>>.appspot.com",
    };

    if(firebase.apps.length == 0) {   // <---Important!!! In lambda, it will cause double initialization.
        firebase.initializeApp(config);
    }

    ...
    <Your Logic here...>
    ...
};

I solved my problem by using firebase REST api

var https = require('https');

exports.handler = function(event, context, callback) {

    var body = JSON.stringify({
        foo: "bar"
    })

   var https = require('https');

var options = {
  host: 'project-XXXXX.firebaseio.com',
  port: 443,
  path: '/.json',
  method: 'POST'
};

var req = https.request(options, function(res) {
  console.log(res.statusCode);
  res.on('data', function(d) {
    process.stdout.write(d);
  });
});
req.end(body);

req.on('error', function(e) {
  console.error(e);
});

    callback(null, "some success message");

}

This is late, but in case someone else is looking:

Zipping your project folder instead of the contents of the project folder can cause this. The zipped folder, when extracted, should not contain a folder with the lambda files in it, but should have the index.js file and the node_modules folder at root level.

A working example of a lambda function is (using latest shiny firebase stuff *sigh*):

var firebase = require('firebase');

// Your service account details
var credentials = {
  "type": "service_account",
  "project_id": "project-123451234512345123",
  "private_key_id": "my1private2key3id",
  "private_key": "-----BEGIN PRIVATE KEY-----InsertKeyHere-----END PRIVATE KEY-----\n",
  "client_email": "projectname@project-123451234512345123.iam.gserviceaccount.com",
  "client_id": "1111222223333344444",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://accounts.google.com/o/oauth2/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/projectname%40project-123451234512345123.iam.gserviceaccount.com"
};

firebase.initializeApp({
  serviceAccount: credentials,
  databaseURL: "https://project-123451234512345123.firebaseio.com"
});

exports.handler = function (event, context, callback) {

  // I use some data passed in from AWS API Gateway:
  if (!event.firebaseUid) {
    callback('Missing param for id');
  }

  firebase.database().ref().child('users').child(firebaseUid).child('at').set(newTokens.access_token).then(function (data) {
    console.log('Firebase data: ', data);
    firebase.database().goOffline();
    callback(null, 'Firebase data: ', data);
  }).catch(function (error) {
    callback('Database set error ' + error);
  });
 };

Now for the caveat. I have experienced this causing the lambda function to timeout even after the firebase callback has happened, ie. the set function seems to create a listener that holds the lambda function open despite return of correct data.

Update: Calling firebase.database().goOffline() fixes the Lambda function timeout issue i was experiencing.

The usual cautions about security not being verified or appropriate, and the possibilities of halting space and time by using this apply.

2017-03-22 edit: google just announced firebase cloud functions, which is a much better way to do this. Cloud functions work just like lambda, and can trigger from firebase events.


Here's my solution using the REST api (so you don't need to require anything):

var https = require('https');
var firebaseHost = "yourapp.firebaseio.com";
function fbGet(key){
  return new Promise((resolve, reject) => {
    var options = {
      hostname: firebaseHost,
      port: 443,
      path: key + ".json",
      method: 'GET'
    };
    var req = https.request(options, function (res) {
      res.setEncoding('utf8');
      var body = '';
      res.on('data', function(chunk) {
        body += chunk;
      });
      res.on('end', function() {
        resolve(JSON.parse(body))
      });
    });
    req.end();
    req.on('error', reject);
  });
}

function fbPut(key, value){
  return new Promise((resolve, reject) => {
    var options = {
      hostname: firebaseHost,
      port: 443,
      path: key + ".json",
      method: 'PUT'
    };

    var req = https.request(options, function (res) {
      console.log("request made")
      res.setEncoding('utf8');
      var body = '';
      res.on('data', function(chunk) {
        body += chunk;
      });
      res.on('end', function() {
        resolve(body)
      });
    });
    req.end(JSON.stringify(value));
    req.on('error', reject);
  });
}

You can use it like this:

fbPut("/foo/bar", "lol").then(res => {
  console.log("wrote data")
})

And then:

fbGet("/foo/bar").then(data => {
  console.log(data); // prints "lol"
}).catch(e => {
  console.log("error saving to firebase: ");
  console.log(e);
})

Another alternative if you're using a node-based development setup is to use the node-lambda package from here. Essentially it provides wrappers to set up, test and deploy to lambda. node-lambda deploy will package up any modules you've installed (e.g. with npm i --save firebase) and make sure they're available on Lambda itself. I've found it really helpful for managing external modules.

For me firebase-admin should do the trick. https://firebase.google.com/docs/admin/setup

Thanks for Josiah Choi for suggesting context.callbackWaitsForEmptyEventLoop though. So lambda doesn't need to initializeFirebase everytimes. My first run was really slow.

  var firebase = require('firebase-admin');
  module.exports.Test = (event, context, callback) => {

  context.callbackWaitsForEmptyEventLoop = false;  //<---Important

  if(firebase.apps.length == 0) {   // <---Important!!! In lambda, it will cause double initialization.
    firebase.initializeApp({
      credential: firebase.credential.cert("serviceAccount.json"),
      databaseURL: <YOUR FIREBASE URL>
    });

  }


 firebase.database().ref('conversation').once('value').then(function(snapshot) {
    console.log (snapshot.val()) ;
   var bodyReturn = {
     input: snapshot.val()
   } ;

   callback(null,bodyReturn);
   context.succeed() ;
});

};

After trying a few things, this seems to work for me (v 3.10.8) :

for(var i=0;i<5;i++)
{

    var firebase = require('firebase');
    var config = {
        apiKey: "",
        authDomain: "",
        databaseURL: "",
        storageBucket: "",
        messagingSenderId: ""
      };
        if(firebase.apps)
        if(firebase.apps.length==0)
      firebase.initializeApp(config)

        firebase.database().ref().child("test").once('value').
          then(function(snapshot) {
            console.log(snapshot.val());

                                 });

  }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!