How to write azure functions which will use express api - node.js

I have a azure function,
In index.js i have the following code
module.exports = function (context, req) {
const createHandler = require('azure-function-express').createHandler;
const app = require('express')();
app.get("/home", (req, res) => {
const y = { "name": "name", "dob": "ddmmyyyy" }
context.res = y
context.done()
});
module.exports = createHandler(app);
context.done();
};
i have function.json :
{
"bindings": [
{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"route": "{*segments}"
},
{
"type": "http",
"direction": "out",
"name": "res"
}
],
"disabled": false
}
i have the above files in my azure function but i am not able get any output i just a blank page if i hit the api end point.
i have to use express to handle many other end points is there any way to handle in azure functions.
when i use nodejs local application setup, i am able to use express and handle many api end points with in a single module is that possible in azure functions? or i have to use different functions for each end point

See code below. We can use Azure function as a common express app.
const createHandler = require('azure-function-express').createHandler;
const app = require('express')();
app.get("/home", (req, res) => {
res.json({ "name": "name", "dob": "ddmmyyyy" });
});
app.get("/work", (req, res) => {
res.json({ "name": req.query.name, "dob": "ddmmyyyy" });
});
module.exports = createHandler(app);
module.exports = function (context, req) and context.done() are no longer useful if azure-function-express is in use. If you want to use other method of context, use req.context instead. See azure-function-express module doc.
Besides, Azure function has a prefix "api" in route by default, if you don't need it(like code above), change it to empty your host.json.
If your function runtime is ~2(beta).
{
"version": "2.0",
"extensions": {
"http": {
"routePrefix": ""
}
}
}
Else in ~1
{
"http": {
"routePrefix": ""
}
}

I have also use try this azure-function-express package but its still in the development and need a-lot of improvement. The best package i found is Azure AWS Severless Express
Package. Its very easy to use and compatible. You can easily use the Express with azure functions

Related

Getting XMLHttpRequest error from HTTP GET request on unformatted JSON

I'm trying to get JSON-data via HTTP from my Dart/Flutter function:
Future<List<News>?> getNews() async {
var client = http.Client();
var uri = Uri.parse('http://localhost:3000/news');
var response = await client.get(uri);
if (response.statusCode == 200) {
var jsonFile = response.body;
try {
return newsFromJson(jsonFile);
} catch (e) {
print(e);
}
}
return null;
}
The Json-File looks like this:
{
"news": [
{
"id": 0,
"title": "Test",
"text": "Test",
"buttonText": "Test",
"source": "Test",
"showButton": false,
"openFile": false,
"openWebsite": true
},
{
"id": 1,
"title": "Test",
"text": "Test",
"buttonText": "Test",
"source": "Test",
"showButton": false,
"openFile": false,
"openWebsite": true
}
]
}
When I start the following Script for the server that is going to provide the data, everything works fine but the json-data is NOT formatted when I call it in the browser:
const express = require('express');
const fs = require('fs');
const app = express();
app.get('/news', (req, res) => {
console.log('Received request');
fs.readFile('data.json', (err, data) => {
if (err) throw err;
const news = JSON.parse(data).news;
res.json(news);
});
});
app.listen(3000, () => {
console.log('Server listening on port 3000');
});
The request from my dart code reaches the NodeJS-Script but ends with the mentioned XMLHttpRequest error. And here comes the interesting thing: When I use the tool json-server (https://github.com/typicode/json-server) with the same json-file, everything IS formatted when calling the url in browser and my Flutter/Dart codes work without any error. So in conclusion: The NodeJS-Script is working like the json-server tool. The only difference is, that the json provided by the NodeJS script isn't formatted in the browser which might causes the error.
Where is the problem?
Could be useful
List<News> newsFromJson(String str) =>
List<News>.from(json.decode(str).map((x) => News.fromJson(x)));
Error: XMLHttpRequest error.
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 299:10 createErrorWithStack
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart 341:28 _throw
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/core/errors.dart 116:5 throwWithStackTrace
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/async/zone.dart 1378:11 callback
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/async/schedule_microtask.dart 40:11 _microtaskLoop
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/async/schedule_microtask.dart 49:5 _startMicrotaskLoop
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 166:15 <fn>
Express.js will try to be compact about the data it sends, rather than pretty.
Format it explicitly:
res.set('Content-Type', 'application/json')
res.send(JSON.stringify(news, undefined, 2));
If you want the JSON data to be formatted exactly as it is in the file, and send the whole file, not a specific part of the JSON, just don't parse it:
res.set('Content-Type', 'application/json');
res.send(data);

How can I fetch CouchDB server version via PouchDB

All I want to get is this object:
{
"couchdb": "Welcome",
"version": "3.1.1",
"git_sha": "ce596c65d",
"uuid": "ff0e85a5e76efdf116e1394e1a94a70f",
"features": [
"access-ready",
"partitioned",
"pluggable-storage-engines",
"reshard",
"scheduler"
],
"vendor": { "name": "The Apache Software Foundation" }
}
But I can't figure out how can I fetch the root server URL.
Maybe there is another option in PouchDB how to get CouchDB server version.
EDIT:
I have found this function is source code, but it doesn't get info described in the comment above the function. GitHub link
// Calls GET on the host, which gets back a JSON string containing
// couchdb: A welcome string
// version: The version of CouchDB it is running
api._info = function (callback) {
setup().then(function () {
return ourFetch(genDBUrl(host, ''));
}).then(function (response) {
return response.json();
}).then(function (info) {
info.host = genDBUrl(host, '');
callback(null, info);
}).catch(callback);
};
After a digging a bit into the source code, I have found the solution.
infoDb
.fetch('/')
.then((res) => {
return res.json();
})
.then((res) => {
console.log('FETCH', res);
});
Result:
{
"couchdb": "Welcome",
"version": "3.1.1",
"git_sha": "ce596c65d",
"uuid": "ff0e85a5e76efdf116e1394e1a94a70f",
"features": [
"access-ready",
"partitioned",
"pluggable-storage-engines",
"reshard",
"scheduler"
],
"vendor": {
"name": "The Apache Software Foundation"
}
}

Using Firebase Functions with Express.js

I am writing a function that accepts input from an API endpoint. But the problem is when I now want to insert the records into the database. Immediately I add the async before the function I get an eslint error of Parsing Error: Unexpected token =>. Because of this, I cannot deploy the function. Kindly also look at the attached screenshot
app.post("/c2b/confirm", async (req, res) => {
console.log("----------confirm-------------");
console.log(JSON.stringify(req.body));
const payment = admin.firestore().collection("payments").doc();
await payment.set(req.body);
res.status(200).json({"ResultCode": 0, "ResultDesc": "Success"});
});
exports.main = functions.https.onRequest(app);
It is a linter error.
Please, be sure you have these in your eslintrc:
{
"env": {
"node": true,
"es6": true
},
"parserOptions": {
"ecmaVersion": 8
}
}

HTTP request failing in AWS Lambda function for Alexa

I am trying to return data from an external api in my inline Lambda function but when I test this in the developer console for Alexa, I get 'There was a problem with the requested skills response' and I can't work out why.
Also, as I am doing this from the AWS console, I can't console.log to see what it actually being returned.
(I have removed the default intents for the sake of the post)
const request = require('request');
const handlers = {
'LaunchRequest': function () {
this.emit(':ask', 'Welcome');
},
'GiveUpdateIntent': function (){
var slot = this.event.request.intent.slots.line.value;
httpGet(slot, (theResult) => {
this.response.speak(theResult);
this.emit(':responseReady');
});
}
};
function httpGet(query, callback) {
var options = {
host: 'api.tfl.gov.uk',
path: '/line/' + encodeURIComponent(query) + '/status',
method: 'GET',
};
var req = http.request(options, res => {
res.setEncoding('utf8');
var responseString = "";
//accept incoming data asynchronously
res.on('data', chunk => {
responseString += chunk;
});
//return the data when streaming is complete
res.on('end', () => {
console.log(responseString[0]);
callback(responseString[0]);
});
});
req.end();
}
exports.handler = function (event, context, callback) {
const alexa = Alexa.handler(event, context, callback);
alexa.APP_ID = APP_ID;
alexa.registerHandlers(handlers);
alexa.execute();
};
"There was a problem with the requested skills response" generally means that the response from your skill was not in the expected format.
Your API request
Ex: vicotria
https://api.tfl.gov.uk/Line/victoria/Status
returns a JSON, and you can't directly pass it Alexa as response. Before you send it back to Alexa, take out status that you actually want Alexa to speak. Then put that into a meaningful sentence that any skill user will understand and send it back.
For example you can return something like:
var speech = "Status severity description for " +
this.event.request.intent.slots.line.value +
" is "
+ responseBody[0].lineStatuses.statusSeverityDescription;
this.emit(':ask',speech, "your re-prompt here");
This is a sample JSON that I got
[
{
"$type": "Tfl.Api.Presentation.Entities.Line, Tfl.Api.Presentation.Entities",
"id": "victoria",
"name": "Victoria",
"modeName": "tube",
"disruptions": [],
"created": "2018-07-31T12:11:08.477Z",
"modified": "2018-07-31T12:11:08.477Z",
"lineStatuses": [
{
"$type": "Tfl.Api.Presentation.Entities.LineStatus, Tfl.Api.Presentation.Entities",
"id": 0,
"statusSeverity": 10,
"statusSeverityDescription": "Good Service",
"created": "0001-01-01T00:00:00",
"validityPeriods": []
}
],
"routeSections": [],
"serviceTypes": [
{
"$type": "Tfl.Api.Presentation.Entities.LineServiceTypeInfo, Tfl.Api.Presentation.Entities",
"name": "Regular",
"uri": "/Line/Route?ids=Victoria&serviceTypes=Regular"
},
{
"$type": "Tfl.Api.Presentation.Entities.LineServiceTypeInfo, Tfl.Api.Presentation.Entities",
"name": "Night",
"uri": "/Line/Route?ids=Victoria&serviceTypes=Night"
}
],
"crowding": {
"$type": "Tfl.Api.Presentation.Entities.Crowding, Tfl.Api.Presentation.Entities"
}
}
]
CloudWatch:
Always make use of CloudWatch to see the logs of your Lambda function, you will get a link under Monitoring tab of your Lambda Function.
Configuring Lambda Test Events: You can test you Lambda code right from your inline editor by configuring Lambda Test Events under Test menu of your inline editor. A function can have up to 10 test events.
This is because your handler is returning before the callback is called. I strongly suggest to move away from callback based development in NodeJS and to use Promise instead.
I just answered a similar question, and provided sample code with promises. Check it here How to make an asynchronous api call for Alexa Skill application with a Lambda function?
The issue turned out to be with using http itself instead of https.
The only response back that I was getting was a status code of 302 which is a redirection because the api I was calling changes all http requests to https.
Therefore, I changed my import to https and used the https.get method (instead of http.get) to call the api and the correct response was returned.

Set Thumbnail image Content-Type

I need to set Content-Type for the thumbnail image. I have tried as shown below.But it is not working.Still, it stores as a stream.
Azure function:
index.json
var Jimp = require("jimp");
module.exports = (context, myBlob) => {
// Read image with Jimp
Jimp.read(myBlob).then((image) => {
// Manipulate image
image
.resize(200, Jimp.AUTO)
.greyscale()
.getBuffer(Jimp.MIME_JPEG, (error, stream) => {
if (error) {
context.log(`There was an error processing the image.`);
context.done(error);
}
else {
context.log(`Successfully processed the image`);
stream.set("Content-Type", Jimp.MIME_JPEG); // here need to set the Content-Type
context.done(null, stream);
}
});
});
};
function.json
{
"bindings": [
{
"name": "myBlob",
"type": "blobTrigger",
"direction": "in",
"path": "project2-photos-original/{name}",
"connection": "thumbnailfunction_STORAGE",
"dataType": "binary"
},
{
"type": "blob",
"name": "$return",
"path": "project2-photos-thumbnail/{name}",
"connection": "thumbnailfunction_STORAGE",
"direction": "out"
}
],
"disabled": false
}
I have seen the same implementation like this on NodeJs
var Jimp = require("jimp");
var express = require("express");
var app = express();
app.get("/my-dynamic-image", function(req, res){
Jimp.read("lenna.png", function(err, lenna) {
lenna.resize(64, 64).quality(60).getBuffer(Jimp.MIME_JPEG, function(err, buffer){
res.set("Content-Type", Jimp.MIME_JPEG);
res.send(buffer);
});
});
});
app.listen(3000);
Question: Can you tell me how to set Content-Type on the Azure function?
p.s. I'm not a Nodejs developer.
EDIT:
Unfortunately the blob output binding for node does not support setting a content type. One option would be to drop the output binding and use the azure storage sdk natively in your node function which should give you the control you need.
If using an Http trigger and output binding:
An express-like 'res' object can be accessed via content.res, so instead of stream.set you'll want context.res.set / context.res.type. The stream object returned in the getBuffer callback is a buffer, not a stream, and has nothing to do with the http response.
One thing to note is that azure functions does not support returning of streams from node yet - you'll need to have the entire buffer (which, luckily, getBuffer appears to return!)
Here is a getBuffer callback:
function(err, buffer){
if (err) {
context.log("There was an error processing the image.");
context.done(err);
}
else {
context.log("Successfully processed the image");
// set content type to Jimp.MIME_JPEG
context.res.type(Jimp.MIME_JPEG)
// send the raw response (don't apply any content negotiation)
context.res.raw(buffer);
}
});

Resources