Serverless Framework with Azure functions - node.js

I am writing services with Serverless Framework & Azure Functions. Examples out there are very simple. But when I try to take a step further, I run into problem. Currently learning from AWS Lambda and then trying to implement it on Azure Functions.
The goal of doing so is:
1) Implement functions as es6 classes and then building the project with webpack.
2) Find a right project structure, which makes more sense.
3) Follow SoC pattern.
I have created a github project https://github.com/GeekOnGadgets/serverless-azure-settings and when I try to build this project serverless package it creates .serverless folder and inside it there is .zip file (the compiled version). Which I understand gets deployed to azure when you run serverless deploy. But when I check on Azure the function is just development code and not the compiled one (please refer to the code below).
Can someone please help with this. Any suggestions is appreciated.
import Settings from './src/Settings/Settings'
module.exports.settings = (event, context, callback) => {
let settings = new Settings();
const response = {
statusCode: 200,
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(settings.dev()),
};
callback(null, response);
}

Indeed javascript azure functions run on nodejs so commonjs modules are the natural format. Node also natively supports much of ES6, though the Functions version of node might not be the latest.
however, there is a current speed issue with loading all the dependencies in node_modules. This is due to file access so a workaround exists to bundle everything into a single script which package.json -> main points to.
I cant comment on how that fits in with serverless, but perhaps this will help clarify.

As far as I know, Node.js still does not support import/export ES6 syntax for modules. See also here.
Try a new deploy changing from
import Settings from './src/Settings/Settings'
to
const Settings = require('./src/Settings/Settings')

Related

Firebase functions - Failed to retrieve function source code

I get the error: "Failed to retrieve function source code" when I try and deploy a function.
This is all from the command line. I am using node 6.11.5 (but in the firebase-admin package.json file in the nodes folder it is says node 6.9.1 is used to download that). I am using firebase-admin#5.8.1 and firebase-functions#0.8.1.
This is the code in my index.js file that I am trying to deploy:
const functions = require('firebase-functions');
exports.helloWorld = functions.https.onRequest((request, response) => {
response.send("Hello from Firebase!");
});
I have also tried to deploy many different things.
Two interesting things:
- I used to be able to deploy any function without problem. This changed about a month ago and now every function I try gets this error. I can't remember making any change that would be related to this.
- Also I can deploy functions from my computer (with the exact same set up and firebase versions) to other projects in the same google account and different google accounts without any problem.
Thanks
I think you should check your billing settings in google cloud. I got the same problem and after updating billing information then redeploy the function, the error is gone.

AWS Lambda function to connect to a Postgresql database

Does anyone know how I can connect to a PostgreSQL database through an AWS Lambda function. I searched it up online but I couldn't find anything about it. If you could tell me how to go about it that would be great.
If you can find something wrong with my code (node.js) that would be great otherwise can you tell me how to go about it?
exports.handler = (event, context, callback) => {
"use strict"
const pg = require('pg');
const connectionStr =
"postgres://username:password#host:port/db_name";
var client = new pg.Client(connectionStr);
client.connect(function(err){
if(err) {
callback(err)
}
callback(null, 'Connection established');
});
context.callbackWaitsForEmptyEventLoop = false;
};
The code throws an error:
cannot find module 'pg'
I wrote it directly on AWS Lambda and didn't upload anything if that makes a difference.
I wrote it directly on AWS Lambda and didn't upload anything if that makes a difference.
Yes this makes the difference! Lambda doesnt provide 3rd party libraries out of the box. As soon as you have a dependency on a 3rd party library you need to zip and upload your Lambda code manually or with the use of the API.
Fore more informations: Lambda Execution Environment and Available Libraries
You need to refer Creating a Deployment Package (Node.js)
Simple scenario – If your custom code requires only the AWS SDK library, then you can use the inline editor in the AWS Lambda console. Using the console, you can edit and upload your code to AWS Lambda. The console will zip up your code with the relevant configuration information into a deployment package that the Lambda service can run.
and
Advanced scenario – If you are writing code that uses other resources, such as a graphics library for image processing, or you want to use the AWS CLI instead of the console, you need to first create the Lambda function deployment package, and then use the console or the CLI to upload the package.
Your case like mine falls under Advanced scenario. So we need to create a deployment package and then upload it. Here what I did -
mkdir deployment
cd deployment
vi index.js
write your lambda code in this file. Make sure your handler name is index.handler when you create it.
npm install pg
You should see node_modules directory created in deployment directory which has multiple modules in it
Package the deployment directory into a zip file and upload to Lambda.
You should be good then
NOTE : npm install will install node modules in same directory under node_modules directory unless it sees a node_module directory in parent directory. To be same first do npm init followed by npm install to ensure modules are installed in same directory for deployment.

How can I split Cloud Functions for Firebase into many files? [duplicate]

Today Firebase released its brand new product Cloud Functions for Firebase and I just created a hello world function and deploy it on my existing firebase project.
It looks like it bundles all dependencies and upload it to firebase just like aws lambda function does. But it takes too much time to be done even on minor changes in code and also need a good connectivity of internet . If you are offline for some reason, you are just in dark what code you are writing until you have a way to execute and test that functions offline on your local machine.
Is there any way to test Cloud Functions for Firebase locally?
firebaser here
Deployment of your Functions indeed takes more time than what I'm normally willing to wait for. We're working hard to improve that and (as Brendan said) are working on a local emulator.
But for the moment, I mostly write my actual business logic into a separate Node script first. That way I can test it from a local command prompt with node speech.js. Once I'm satisfied that the function works, I either copy/paste it into my actual Functions file or (better) import the speech module into my functions file and invoke it from there.
One abbreviated example that I quickly dug up is when I was wiring up text extraction using the Cloud Vision API. I have a file called ocr.js that contains:
var fetch = require('node-fetch');
function extract_text(url, gcloud_authorization) {
console.log('extract_text from image '+url+' with authorization '+gcloud_authorization);
return fetch(url).then(function(res) {
return res.buffer();
}).then(function(buffer) {
return fetch('https://vision.googleapis.com/v1/images:annotate?key='+gcloud_authorization, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
"requests":[
{
"image":{
"content": buffer.toString('base64')
},
"features":[
{
"type":"TEXT_DETECTION",
"maxResults":1
}
]
}
]
})
});
}).then(function(res) {
var json = res.json();
if (res.status >= 200 && res.status < 300) {
return json;
} else {
return json.then(Promise.reject.bind(Promise));
}
}).then(function(json) {
if (json.responses && json.responses.length && json.responses[0].error) {
return Promise.reject(json.responses[0].error);
}
return json.responses[0].textAnnotations[0].description;
});
}
if (process.argv.length > 2) {
// by passing the image URL and gcloud access token, you can test this module
process.argv.forEach(a => console.log(a));
extract_text(
process.argv[2], // image URL
process.argv[3] // gcloud access token or API key
).then(function(description) {
console.log(description);
}).catch(function(error) {
console.error(error);
});
}
exports.extract_text = extract_text;
And then in my Functions index.js, I have:
var functions = require('firebase-functions');
var fetch = require('node-fetch');
var ocr = require('./ocr.js');
exports.ocr = functions.database().path('/messages/{room}/{id}').onWrite(function(event) {
console.log('OCR triggered for /messages/'+event.params.room+'/'+event.params.id);
if (!event.data || !event.data.exists()) return;
if (event.data.ocr) return;
if (event.data.val().text.indexOf("https://firebasestorage.googleapis.com/") !== 0) return; // only OCR images
console.log(JSON.stringify(functions.env));
return ocr.extract_text(event.data.val().text, functions.env.googlecloud.apikey).then(function(text) {
return event.data.adminRef.update({ ocr: text });
});
});
So as you can see this last file is really just about wiring up the "worker method" ocr.extract_text to the database location.
Note this is a project from a while ago, so some of the syntax (mostly the functions.env part) might have changed a bit.
firebaser here
To debug your Cloud Functions for Firebase locally, there is an emulator. See the documentation for more info.
run and debug/inspect functions locally
prerequisites (google-cloud functions and firebase-specific):
npm install -g #google-cloud/functions-emulator
npm install --save firebase-functions
npm install -g firebase-tools
To run and inspect/debug: first run functions locally, then inspect each function, and finally run each specific function to debug+inspect it. Use functions start as an alternative to firebase serve and note the documentation for each tool is available (and useful).
To run and debug the specific function myFn as-expected (eg in Nodejs via chrome://inspect and note this works using Nodejs v10 though not officially supported):
firebase serve --only functions
functions inspect myFn
functions call myFn # or call from browser
additional documentation:
https://firebase.google.com/docs/functions/local-emulator
https://cloud.google.com/functions/docs/emulator#debug-emulator
https://github.com/GoogleCloudPlatform/cloud-functions-emulator/wiki
>> Is there any way to test Cloud Functions for Firebase locally?
You can use the following command to start a firebase shell (execute in your functions directory):
npm run build && firebase functions:shell
You can invoke your functions in the shell like so:
helloWorld()
Refer this link for more information.
Answered here: https://github.com/firebase/firebase-functions/issues/4#issuecomment-286515989
Google Cloud Functions also open-sourced a local emulator, and we are
working to build a tighter integration with Cloud Functions for
Firebase. In the meanwhile, you can check it at here:
https://github.com/GoogleCloudPlatform/cloud-functions-emulator/
The emulator does allow you to run functions locally. Here's the
documentation that explains how to use it:
https://cloud.google.com/functions/docs/emulator
I couldn't get the single stepping working at first. My process was the same as documented in many answers here.
Also, these pages contain nearly all the documentation I required:
https://firebase.google.com/docs/functions/local-emulator
https://cloud.google.com/functions/docs/emulator#debugging_with_the_emulator
I had got the functions running using firebase serve --only functions, but hadn't got the debugger up and running. Then I came across the other way of directly using the emulator and managed to hit a break point like this:
# start the emulator
functions start
# allow inspection
functions inspect helloWorld
# call the function from the cli
functions call helloWorld
This worked, and I could hit a breakpoint.
However, when hitting the endpoint for the function in postman or the browser, I got no response at all.
The step I was missing was:
# deploy the function to the emulator
functions deploy helloWorld --trigger-http
# you need to toggle inspection after the deploy
functions inspect helloWorld
Now I can hit the endpoint for the function from postman or the browser, and the breakpoint is hit.
I recommend the brilliant NiM chrome extension for debugging and hope this answer helps someone, even if this is an old question.
Firstly, I suggest you to install following dependencies,
npm install --save firebase-functions
npm install -g firebase-tools
If already installed then you can update it to latest one. Generally, functions-emulator comes with above dependency but still I would recommend you to update it,
npm install -g #google-cloud/functions-emulator
Once it has been updated, go to functions folder of you application and run following command,
firebase serve --only functions
I hope it helps!
For vscode users debugging HTTP functions (webhooks, etc)...
The google cloud emulator (firebase serve --only functions) launches a separate process to run your functions. You can attach to this process with vscode, but since the emulator only creates this process after the first function is called, it's not straightforward.
create a dummy HTTP endpoint in your functions which will return the processID:
app.get("/processid", function(request, response) {
response.send(`${process.pid}`);
});
start the emulator with firebase serve --only functions
call the http://<localhost_url>/processid endpoint. This will create the process and return the processID
use vscode to attach to the specified process. You can now set breakpoints, step, etc on any of the other functions (they all use the same process).
There's probably a nicer way to glue all this together.
There is now a cloud functions emulator that lets you call functions locally
Once I have completed my PoC I will update this answer to include code and steps I used.

how to test Cloud Functions for Firebase locally on pc

Today Firebase released its brand new product Cloud Functions for Firebase and I just created a hello world function and deploy it on my existing firebase project.
It looks like it bundles all dependencies and upload it to firebase just like aws lambda function does. But it takes too much time to be done even on minor changes in code and also need a good connectivity of internet . If you are offline for some reason, you are just in dark what code you are writing until you have a way to execute and test that functions offline on your local machine.
Is there any way to test Cloud Functions for Firebase locally?
firebaser here
Deployment of your Functions indeed takes more time than what I'm normally willing to wait for. We're working hard to improve that and (as Brendan said) are working on a local emulator.
But for the moment, I mostly write my actual business logic into a separate Node script first. That way I can test it from a local command prompt with node speech.js. Once I'm satisfied that the function works, I either copy/paste it into my actual Functions file or (better) import the speech module into my functions file and invoke it from there.
One abbreviated example that I quickly dug up is when I was wiring up text extraction using the Cloud Vision API. I have a file called ocr.js that contains:
var fetch = require('node-fetch');
function extract_text(url, gcloud_authorization) {
console.log('extract_text from image '+url+' with authorization '+gcloud_authorization);
return fetch(url).then(function(res) {
return res.buffer();
}).then(function(buffer) {
return fetch('https://vision.googleapis.com/v1/images:annotate?key='+gcloud_authorization, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
"requests":[
{
"image":{
"content": buffer.toString('base64')
},
"features":[
{
"type":"TEXT_DETECTION",
"maxResults":1
}
]
}
]
})
});
}).then(function(res) {
var json = res.json();
if (res.status >= 200 && res.status < 300) {
return json;
} else {
return json.then(Promise.reject.bind(Promise));
}
}).then(function(json) {
if (json.responses && json.responses.length && json.responses[0].error) {
return Promise.reject(json.responses[0].error);
}
return json.responses[0].textAnnotations[0].description;
});
}
if (process.argv.length > 2) {
// by passing the image URL and gcloud access token, you can test this module
process.argv.forEach(a => console.log(a));
extract_text(
process.argv[2], // image URL
process.argv[3] // gcloud access token or API key
).then(function(description) {
console.log(description);
}).catch(function(error) {
console.error(error);
});
}
exports.extract_text = extract_text;
And then in my Functions index.js, I have:
var functions = require('firebase-functions');
var fetch = require('node-fetch');
var ocr = require('./ocr.js');
exports.ocr = functions.database().path('/messages/{room}/{id}').onWrite(function(event) {
console.log('OCR triggered for /messages/'+event.params.room+'/'+event.params.id);
if (!event.data || !event.data.exists()) return;
if (event.data.ocr) return;
if (event.data.val().text.indexOf("https://firebasestorage.googleapis.com/") !== 0) return; // only OCR images
console.log(JSON.stringify(functions.env));
return ocr.extract_text(event.data.val().text, functions.env.googlecloud.apikey).then(function(text) {
return event.data.adminRef.update({ ocr: text });
});
});
So as you can see this last file is really just about wiring up the "worker method" ocr.extract_text to the database location.
Note this is a project from a while ago, so some of the syntax (mostly the functions.env part) might have changed a bit.
firebaser here
To debug your Cloud Functions for Firebase locally, there is an emulator. See the documentation for more info.
run and debug/inspect functions locally
prerequisites (google-cloud functions and firebase-specific):
npm install -g #google-cloud/functions-emulator
npm install --save firebase-functions
npm install -g firebase-tools
To run and inspect/debug: first run functions locally, then inspect each function, and finally run each specific function to debug+inspect it. Use functions start as an alternative to firebase serve and note the documentation for each tool is available (and useful).
To run and debug the specific function myFn as-expected (eg in Nodejs via chrome://inspect and note this works using Nodejs v10 though not officially supported):
firebase serve --only functions
functions inspect myFn
functions call myFn # or call from browser
additional documentation:
https://firebase.google.com/docs/functions/local-emulator
https://cloud.google.com/functions/docs/emulator#debug-emulator
https://github.com/GoogleCloudPlatform/cloud-functions-emulator/wiki
>> Is there any way to test Cloud Functions for Firebase locally?
You can use the following command to start a firebase shell (execute in your functions directory):
npm run build && firebase functions:shell
You can invoke your functions in the shell like so:
helloWorld()
Refer this link for more information.
Answered here: https://github.com/firebase/firebase-functions/issues/4#issuecomment-286515989
Google Cloud Functions also open-sourced a local emulator, and we are
working to build a tighter integration with Cloud Functions for
Firebase. In the meanwhile, you can check it at here:
https://github.com/GoogleCloudPlatform/cloud-functions-emulator/
The emulator does allow you to run functions locally. Here's the
documentation that explains how to use it:
https://cloud.google.com/functions/docs/emulator
I couldn't get the single stepping working at first. My process was the same as documented in many answers here.
Also, these pages contain nearly all the documentation I required:
https://firebase.google.com/docs/functions/local-emulator
https://cloud.google.com/functions/docs/emulator#debugging_with_the_emulator
I had got the functions running using firebase serve --only functions, but hadn't got the debugger up and running. Then I came across the other way of directly using the emulator and managed to hit a break point like this:
# start the emulator
functions start
# allow inspection
functions inspect helloWorld
# call the function from the cli
functions call helloWorld
This worked, and I could hit a breakpoint.
However, when hitting the endpoint for the function in postman or the browser, I got no response at all.
The step I was missing was:
# deploy the function to the emulator
functions deploy helloWorld --trigger-http
# you need to toggle inspection after the deploy
functions inspect helloWorld
Now I can hit the endpoint for the function from postman or the browser, and the breakpoint is hit.
I recommend the brilliant NiM chrome extension for debugging and hope this answer helps someone, even if this is an old question.
Firstly, I suggest you to install following dependencies,
npm install --save firebase-functions
npm install -g firebase-tools
If already installed then you can update it to latest one. Generally, functions-emulator comes with above dependency but still I would recommend you to update it,
npm install -g #google-cloud/functions-emulator
Once it has been updated, go to functions folder of you application and run following command,
firebase serve --only functions
I hope it helps!
For vscode users debugging HTTP functions (webhooks, etc)...
The google cloud emulator (firebase serve --only functions) launches a separate process to run your functions. You can attach to this process with vscode, but since the emulator only creates this process after the first function is called, it's not straightforward.
create a dummy HTTP endpoint in your functions which will return the processID:
app.get("/processid", function(request, response) {
response.send(`${process.pid}`);
});
start the emulator with firebase serve --only functions
call the http://<localhost_url>/processid endpoint. This will create the process and return the processID
use vscode to attach to the specified process. You can now set breakpoints, step, etc on any of the other functions (they all use the same process).
There's probably a nicer way to glue all this together.
There is now a cloud functions emulator that lets you call functions locally
Once I have completed my PoC I will update this answer to include code and steps I used.

Deployment of Node native modules to Azure WebSites

I'm trying to deploy the following sample node application to an Azure website:
var http = require('http');
var port = process.env.port || 1337;
http.createServer(function (req, res) {
res.writeHead(200, { 'Content-Type': 'text/html' });
try {
var canvasModule = require('canvas'),
canvas = new canvasModule(200, 200),
ctx = canvas.getContext('2d');
ctx.rotate(0.5);
ctx.font = '30px Impact';
var message = "Hello World";
ctx.fillText(message, 40, 40);
ctx.strokeRect(30, 5, 160, 50);
res.end('<html><img src="' + canvas.toDataURL() + '" /></html>');
} catch (e) {
res.end('Error: ' + e);
}
}).listen(port);
The tricky part is that I'm using a node module called "canvas" that needs to be compiled at install time using node-gyp.
According to this Microsoft page native modules are not supported on Azure Web-sites and the compiled version of the module should be copied to Azure for the app to work properly:
Native Modules
While most modules are simply plain-text JavaScript files, some
modules are platform-specific binary images. These modules are
compiled at install time, usually by using Python and node-gyp. One
specific limitation of Azure Web Sites is that while it natively
understands how to install modules specified in a package.json or
npm-shrinkwrap.json file, it does not provide Python or node-gyp and
cannot build native modules.
Since Azure Cloud Services rely on the node_modules folder being
deployed as part of the application, any native module included as
part of the installed modules should work in a cloud service as long
as it was installed and compiled on a Windows development system.
Native modules are not supported with Azure Web Sites. Some modules
such as JSDOM and MongoDB have optional native dependencies, and will
work with applications hosted in Azure Web Sites.
I deployed everything to Azure (using the publishing Wizard) and all the files were sent to Azure (apparently at least), including the compiled modules. When running the site on Azure I obtain the following exception:
Error: The specified module could not be found.
D:\home\site\wwwroot\node_modules\canvas\build\Release\canvas.node
But that file is in-fact deployed on the server:
Creating a VM would obviously be an option but I would prefer to use web-sites. Anyone has any idea that I can try out?
The error message ""The specified module could not be found" is a bit misleading. It can mean one or more dependent dlls are not found.
I assumed you used the build instruction in Windows. If you built canvas against the GTK2 zip file in the instruction, you can copy %GTKDIR%\bin\freetype6.dll to %YourApp%\node_modules\canvas\build\Release. This dll is not copied during the build process. You would need to do this deploying to a VM as well. Or you can build canvas against the Windows GTK3 bundle.
And unfortunately even after you have all the dll, the sandbox of Azure Websites seems to prevent canvas from running. Hopefully it would be enabled in the near future.
Alright got this working today!
You need to deploy your node_modules directory, in addition to that you need to copy all the contents gtk/bin to the root of your application, which makes it a bit messy, but it works. I imagine you could change the path to look elsewhere but I needed to get the app running so I didn't investigate any further.
That last line in #mtian's answser is key. You can spend time getting it to deploy but ultimately the Azure Website VMs lack the GDI+ APIs that node-canvas relies on to execute (presumably blocked for security considerations). If you want node-canvas on Azure, you can't use Azure Websites and will instead need to setup and manage your own VMs.

Resources