Axios request within /POST request on Express - node.js

As you can see below, in my server.js file I have a /POST Info request that gets called on a form submittal.
I started to get confused on reading about the different between app.post and express routes and if in anyway using routes would benefit my code here.
Within the /POST Info I have two axios requests to 2 different APIs and I think it would be wise to move the code elsewhere to make it cleaner.
Would knowing how routes work here benefit me anyway?And if you can explain the difference here that would be great.
app.post('/Info', function (req, res) {
var State = req.body.State;
var income = Number(req.body.income);
var zip = req.body.ZIP;
axios.post('https://taxee.io/api/v2/calculate/2017', {
//data sent to Taxee.io
"exemptions": 1
, "filing_status": "single"
, "pay_periods": 1
, "pay_rate": income || 100000
, "state": State || "NY"
}, {
headers: {
'Authorization': "Bearer <API_KEY>"
//headers
}
}).then(function (response) {
var obj = {
income: '$' + income
, fica: response.data.annual.fica.amount
, federal: response.data.annual.federal.amount
, residence: State + ", " + zip
, state: response.data.annual.state.amount
}
axios.get("https://www.quandl.com/api/v3/datasets/ZILL/Z" + zip + "_RMP.json?api_key=<API_KEY>").then(function (response) {
var monthRent = response.data.dataset.data[0][1]
obj.rent = monthRent
obj.yearlyRent = Number(monthRent) * 12;
}).then(function (response) {
res.send(obj);
});
}).catch(function (error) {
alert('error');
});
}

There are two ways to define routes in an Express application:
Use the Express application (app) object directly:
const express = require('express')
const app = express()
app.post(...)
app.get(...)
app.put(...)
// and so on
Or use the router object:
const express = require('express')
const app = express()
const router = express.Router()
router.post(...)
router.get(...)
router.put(...)
// and so on
app.use(router)
My guess is that you've been reading about the latter snippet of code with the router object. Using Express' Router object can indeed make code cleaner to read as there more of a separation of concerns.
There's nothing wrong with calling an external API from your own API. For example, in a project of mine, I call the Google Calendar API on this line. The only difference between mine is yours is that I used the Google APIs Node.js Client while you used standard HTTP requests. I could have certainly used HTTP requests as shown here.
Your code is fine, but can be improved. For example, instead of:
axios.post('...', {
exemptions: 1,
filing_status: 'single',
pay_periods: 1,
pay_rate: income || 100000,
state: State || 'NY'
})
You could call an helper function that prepares the options object:
function prepareOptions (state = 'NY', income = 100000) {
return {
exemptions: 1,
filing_status: 'single',
pay_periods: 1,
pay_rate: income,
state: State
}
}
Then call it like so:
axios.post('...', prepareOptions(State, income))
This makes for more readable code.
Finally, there is no reason to use axios on the server side. Simply use Node's built in HTTP module.

app.post('/Info', function (req, res) {
var uData ={
state: req.body.State,
income : Number(req.body.income),
zip: req.body.ZIP
};
taxee(uData).then(function(data){
return rent(data) ;
}).then(function(fullData){
res.send(fullData);
}).catch(function (error) {
res.render('error');
});
function taxee(data) {
return new Promise((resolve, reject) => {
var income = data.income;
var state = data.state;
var zip = data.zip;
axios.post('https://taxee.io/api/v2/calculate/2017', {
//data sent to Taxee.io
"exemptions": 1
, "filing_status": "single"
, "pay_periods": 1
, "pay_rate": income || 100000
, "state": state || "NY"
, }, header).then(function (response) {
var taxData = {
income: '$' + income
, fica: response.data.annual.fica.amount
, federal: response.data.annual.federal.amount
, stateTax: response.data.annual.state.amount
, state
, zip: zip
}
resolve(taxData);
}).catch(function (error) {
console.log('break');
resolve(error);
});
});
};
function rent(data) {
return new Promise((resolve, reject) => {
axios.get("https://www.quandl.com/api/v3/datasets/ZILL/Z" + data.zip + "_RMP.json?api_key=d7xQahcKCtWUC4CM1LVd").then(function (response) {
console.log(response.status, ' status');
var monthRent = response.data.dataset.data[0][1];
data.rent = monthRent
data.yearlyRent = Number(monthRent) * 12;
return data;
}).then(function (response) {
resolve( data);
}).catch(function (error) {
reject(error);
});
});
}
module.exports = {
taxee
, rent
};
Ended up putting the code above into clean promise methods. Really happy how it worked out!

Related

WA business api nodejs

I have a problem with my nodejs code and the connection to the official whatsapp business api.
The bot connects the webhook correctly, the messages arrive to the server correctly but the code I have implemented to make it respond is not being effective, I checked the code from top to bottom but I can't find the fault.
I leave you the codes so you have more context:
whatsappController.js:
const fs = require("fs");
const myConsole = new console.Console(fs.createWriteStream("./logs.txt"));
const whatsappService = require("../services/whatsappService")
const VerifyToken = (req, res) => {
try {
var accessToken = "456E7GR****************************";
var token = req.query["hub.verify_token"];
var challenge = req.query["hub.challenge"];
if(challenge != null && token != null && token == accessToken){
res.send(challenge);
}
else{
res.status(400).send();
}
} catch(e) {
res.status(400).send();
}
}
const ReceivedMessage = (req, res) => {
try {
var entry = (req.body["entry"])[0];
var changes = (entry["changes"])[0];
var value = changes["value"];
var messageObject = value["messages"];
if(typeof messageObject != "undefined"){
var messages = messageObject[0];
var text = GetTextUser(messages);
var number = messages["from"];
myConsole.log("Message: " + text + " from: " + number);
whatsappService.SendMessageWhatsApp("The user say: " + text, number);
myConsole.log(messages);
myConsole.log(messageObject);
}
res.send("EVENT_RECEIVED");
}catch(e) {
myConsole.log(e);
res.send("EVENT_RECEIVED");
}
}
function GetTextUser(messages){
var text = "";
var typeMessage = messages["type"];
if(typeMessage == "text"){
text = (messages["text"])["body"];
}
else if(typeMessage == "interactive"){
var interactiveObject = messages["interactive"];
var typeInteractive = interactiveObject["type"];
if(typeInteractive == "button_reply"){
text = (interactiveObject["button_reply"])["title"];
}
else if(typeInteractive == "list_reply"){
text = (interactiveObject["list_reply"])["title"];
}else{
myConsole.log("sin mensaje");
}
}else{
myConsole.log("sin mensaje");
}
return text;
}
module.exports = {
VerifyToken,
ReceivedMessage
}
The second file is whatsappService which I make the connection with the api using the token and I also send the format of the message I want to send when I receive a hello for example...
const https = require("https");
function SendMessageWhatsApp(textResponse, number){
const data = JSON.stringify({
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": number,
"type": "text",
"text": {
"preview_url": false,
"body": textResponse
}
});
const options = {
host:"graph.facebook.com",
path:"/v15.0/1119744*************/messages",
method:"POST",
body:data,
headers: {
"Content-Type":"application/json",
Authorization:"Bearer EAAWNbICfuWEBAK5ObPbD******************************************************"
}
};
const req = https.request(options, res => {
res.on("data", d=> {
process.stdout.write(d);
});
});
req.on("error", error => {
console.error(error);
});
req.write(data);
req.end();
}
module.exports = {
SendMessageWhatsApp
};
Then I declare the routes for the get (to check token) and post (to receive and reply to messages) methods:
const expres = require("express");
const router = expres.Router();
const whatsappController = require("../controllers/whatsappControllers");
router
.get("/", whatsappController.VerifyToken)
.post("/", whatsappController.ReceivedMessage)
module.exports = router;
Last but not least the index file for the code to run correctly:
const express = require("express");
const apiRoute = require("./routes/routes");
const app = express();
const PORT = process.env.PORT || 3000
app.use(express.json());
app.use("/whatsapp", apiRoute);
app.listen(PORT, () => (console.log("El puerto es: " + PORT)));
I should clarify that I did the tests with Postman and they were all successful, it responds and receives messages correctly, finally I did the tests by uploading the bot to the Azure service and it works without problem until it has to answer/replicate the user's message.
The bot is not responding to the user when he talks to it but everything arrives correctly to the server and it processes it with a 200 response. I attach the evidence that there is no problem in the reception.
Finally I must say that in the meta platform I have everything configured as specified by the same platform, I have already configured the api to answer the messages through the webhooks and everything is correct, I just can't get the bot to answer correctly.
The bot is hosted in the Azure service.
Solved: some numbers have a problema with the api of WAB in my country (Argentina) the phone numbers start in +54 9 11. The problem is the 9 in the phone number, and this have a conflict in the servers of meta, Solution quit number 9 to numbers of this country and the message will send to user.

Weather Data in express and node.js not printing in my command line

const { response } = require("response");
const express = require("express");
const https = require("https")
const app = express ();
app.get("/", function(req,res) {
const url = "https://api.openweathermap.org/data/2.5/weather?q=London&applied=536bcef96b2f01cd9b9f076db90807fe&unit=metric";
https.get(url, function(response) {
console.log(response.statusCode);
})
response.on("data", function(data) {
const weatherData = JSON.parse(data)
console.log(weatherData);
})
res.send("Welcome to the future");
})
app.listen(3000, function() {
console.log("listening on port 3000");
})
The problem here is that when I type
response.on to get data from the url
to print it in the command line, it
brings const { response } = require
("express") as shown above which is
very alien to me.
Please, how do I fix it so I can get
my weatherData printed in the CMD?
There are quite a few things you'll need to change.
First, this section is wrong:
https.get(url, function(response) {
console.log(response.statusCode);
})
response.on("data", function(data) {
const weatherData = JSON.parse(data)
console.log(weatherData);
})
Since "response" is a parameter you receive from the callback on the "get" function, you need to declare the "response.on" inside the funcion scope, like this:
https.get(url, function(response) {
console.log(response.statusCode);
response.on("data", function(data) {
const weatherData = JSON.parse(data)
console.log(weatherData);
})
})
Also, the "data" event only delivers a chunk of data. You should be listening for an "end" event aswell, and only parse the data when you receive the "end" event
https.get(url, function(response) {
console.log(response.statusCode);
const result = []
response.on("data", function(data) {
result.push(data);
})
.on("end", function() {
const weatherData = JSON.parse(result.join(""));
console.log(weatherData);
})
})
And since you're not using the module named "response", you also need to remove this:
const { response } = require("response");
And then correct all the typos that were already mentioned in the comments, which were:
Add the missing quote " at require("express) on line 2
Remove the extra backsting at console.log("listening on port 3000")`; on line 17
Change the second query parameter on your URL on line 6 from "applied" to "appid"
First confirm your url is valid
https://api.openweathermap.org/data/2.5/weather?q=London&appid=536bcef96b2f01cd9b9f076db90807fe&unit=metric
If you are using windows 10 or 11 you don't need all those responses simply try this at cmd line (NOTE you need for each & within the url to escape with ^ like this ^&)
curl -o current.txt https://api.openweathermap.org/data/2.5/weather?q=London^&appid=536bcef96b2f01cd9b9f076db90807fe^&unit=metric
type current.txt
you could include both in one line but for the &type that last & does not need ^escape
curl -o current.txt https://api.openweathermap.org/data/2.5/weather?q=London^&appid=536bcef96b2f01cd9b9f076db90807fe^&unit=metric&type current.txt
after the download you should see the response in the console.
so you can call that command any way you wish (hidden or not) and or read the text file on the screen or in any application you choose.
Current.txt
{"coord":{"lon":-0.1257,"lat":51.5085},"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"base":"stations","main":{"temp":276.78,"feels_like":272.23,"temp_min":275.76,"temp_max":278.14,"pressure":999,"humidity":84},"visibility":10000,"wind":{"speed":6.17,"deg":250},"clouds":{"all":9},"dt":1641696366,"sys":{"type":2,"id":2019646,"country":"GB","sunrise":1641715415,"sunset":1641744666},"timezone":0,"id":2643743,"name":"London","cod":200}
Here's a fairly simple version using the got() library for making the http request. It's a promise-based, higher level library that is just a lot easier to use than the https library which works at a lower level and requires more code to work properly and handle errors.
Here's how you would do this with the got() library:
const got = require("got");
const express = require("express");
const app = express();
app.get("/", async function(req, res) {
try {
const url = "https://api.openweathermap.org/data/2.5/weather?q=London&appid=536bcef96b2f01cd9b9f076db90807fe&unit=metric";
const result = await got(url).json();
console.log(result);
res.json(result);
} catch (e) {
console.log(e);
res.sendStatus(500);
}
});
app.listen(3000, function() {
console.log("listening on port 3000");
});
Changes:
Fixed URL (change applied to appid).
Switch to got() library for the http request and built in JSON parsing
Add error handling
Send result as JSON
This generates the following output:
{
coord: { lon: -0.1257, lat: 51.5085 },
weather: [ { id: 800, main: 'Clear', description: 'clear sky', icon: '01n' } ],
base: 'stations',
main: {
temp: 276,
feels_like: 271.47,
temp_min: 274.33,
temp_max: 277.49,
pressure: 1000,
humidity: 86
},
visibility: 10000,
wind: { speed: 5.66, deg: 250 },
clouds: { all: 8 },
dt: 1641707384,
sys: {
type: 2,
id: 2019646,
country: 'GB',
sunrise: 1641715415,
sunset: 1641744666
},
timezone: 0,
id: 2643743,
name: 'London',
cod: 200
}

Sending HTTP Post request from node to Foxx service (ArangoDB)

I am trying to send a post request from a node + express server to my Foxx service on Arangodb.
On the node side :
var route = arangopi + '/edge/' + col.name ;
var body = {data: data, from: fromId, to: toId} ;
console.log('|| body :', route, body) ;
>> || body : http//XXX/_db/my-DB/my-foxx-service/path/to/visitedBy { data: { isBackup: true, text: '', isHint: true, continuance: 3441.5 }, from: 'Drop/27237133', to: 'Bot/41116378' }
return requestify.post (route, body)
On the Foxx side, I receive the request but the logs tell me it has no body :
router.post('/path/to/:param', function (req, res) {
console.log ('|| body :', req.body)
var data = req.body ;
var result = api.DoSomething (req.stateParams.param, data)
res.send(result)
})
.response(joi.object().required(), 'Entry stored in the collection.')
.summary('Summary')
.description('Description')
>> || body : [Object { "binarySlice" : function binarySlice() { [native code] }, "asciiSlice" : function asciiSlice() { [native code] }, "base64Slice" : function base64Slice() { [native code] }, "ucs2Slice" : function ucs2Slice() { [native code] }, "hexSlice" : f...
On the node side I also tried the 'request' module.
return request.post(route, {form:body}, function (error, response, body) {
console.log('error:', error);
console.log('statusCode:', response && response.statusCode);
console.log('body:', body);
return response ;
});
And I get the same logs from Foxx.
What do I do wrong ?
Here is a screenshot of my operation on the Foxx interface. Is it normal that I cannot specify a request body for testing ?
I think the reason is because you haven't specified in the end point in Foxx that there is a body expected as part of the .post.
It took me a while to work out a way of defining Foxx MicroServices, and I read through a number of ArangoDB example code before I settled on a pattern.
To help you get started, I've provided how I would quickly mock up the Foxx MicroService code in a way that is extensible, allowing you to separate your Routes from your Models.
Use these as examples to get your example working.
I've made assumptions that there are two document collections, 'Drop' and 'Bot' with an edge collection that joins them called 'VisitedBy'.
All these files are stored on your Foxx MicroService:
main.js
'use strict';
module.context.use('/v1/visitedBy', require('./routes/visitedBy'), 'visitedBy');
routes/visitedBy.js
'use strict';
const request = require('#arangodb/request');
const joi = require('joi');
const createRouter = require('#arangodb/foxx/router');
const VisitedBy = require('../models/visitedBy');
const visitedDataSchema = joi.object().required().description('Data that tracks a visited event');
const router = createRouter();
module.exports = router;
/*********************************************
* saveVisitedBy
* Path Params:
* none
* Query Params:
* none
* Body Params:
* body (required) The data that is used to record when something is visited
*/
router.post('/', function (req, res) {
const visitedData = req.body;
const savedData = VisitedBy.saveVisitedByData(VisitedBy.fromClient(visitedData));
if (savedData) {
res.status(200).send(VisitedBy.forClient(savedData));
} else {
res.status(500).send('Data not saved, internal error');
}
}, 'saveVisitedBy')
.body(visitedDataSchema, 'visited data')
.response(VisitedBy.savedDataSchema, 'The response after the data is saved')
.summary('Save visited data')
.description('Save visited data');
models/visitedBy.js
'use strict';
const _ = require('lodash');
const joi = require('joi');
const db = require('#arangodb').db;
const visitedByEdgeCollection = 'VisitedBy';
/*
Schema for a response after saving visitedBy data
*/
const savedDataScema = {
id: joi.string(),
data: joi.object(),
_from: joi.string(),
_to: joi.string()
};
module.exports = {
savedDataSchema: savedDataScema,
forClient(obj) {
// Implement outgoing transformations here
// Remove keys on the base object that do not need to go through to the client
if (obj) {
obj = _.omit(obj, ['_id', '_rev', '_oldRev', '_key']);
}
return obj;
},
fromClient(obj) {
// Implement incoming transformations here
return obj;
},
saveVisitedByData(visitedData) {
const q = db._createStatement({
"query": `
INSERT {
_from: #from,
_to: #to,
data: #data,
date: DATE_NOW()
} IN ##col
RETURN MERGE ({ id: NEW._id }, NEW)
`
});
q.bind('#col', visitedByEdgeCollection);
q.bind('from', visitedData.from);
q.bind('to', visitedData.to);
q.bind('data', visitedData.data);
const res = q.execute().toArray();
return res[0];
}
};
Your service should look like this in the Swagger interface:
You can learn more about using joi to define data structures here.
It takes a bit getting used to joi, but once you get some good working examples you can define great data definitions for incoming and outgoing data.
I hope this helps, it was difficult for me getting a basic MicroService code model that made it clear how things operated, I'm sure a lot can be done for this example but it should be a good starting spot.
As David Thomas explained in his answer, I needed to specify a body format in my router code (Foxx side).
In short :
const bodySchema = joi.object().required().description('Data Format');
router.post('/path/to/:param', function (req, res) {
var data = req.body ;
var result = api.DoSomething (req.stateParams.param, data)
res.send(result)
})
.body(bodySchema, 'Body data')
.response(joi.object().required(), 'Entry stored in the collection.')
.summary('Summary')
.description('Description')

angularjs error on server callback

I'm making a call to the server using resource and when I go to the base URL of
/viewEvent
It works fine. I receive all the database entries. However, when I go to
/viewEvent/1234
where 1234 is the eventID
I get a undefined is not a function and this is a crash from within angular. Stack trace is
TypeError: undefined is not a function
at copy (http://localhost:8000/js/lib/angular/angular.js:593:21)
at http://localhost:8000/js/lib/angular/angular-resource.js:410:19
at wrappedCallback (http://localhost:8000/js/lib/angular/angular.js:6846:59)
at http://localhost:8000/js/lib/angular/angular.js:6883:26
at Object.Scope.$eval (http://localhost:8000/js/lib/angular/angular.js:8057:28)
at Object.Scope.$digest (http://localhost:8000/js/lib/angular/angular.js:7922:25)
at Object.Scope.$apply (http://localhost:8000/js/lib/angular/angular.js:8143:24)
at done (http://localhost:8000/js/lib/angular/angular.js:9170:20)
at completeRequest (http://localhost:8000/js/lib/angular/angular.js:9333:7)
at XMLHttpRequest.xhr.onreadystatechange (http://localhost:8000/js/lib/angular/angular.js:9303:11) angular.js:575
When I examine the server, the request was made correctly. I can see that it got 1234 and it pulls the correct entry from the mongo database.
This is the controller logic
.controller("viewEventsController", ["$scope", 'EventService', '$location', function($scope, EventService, $location){
var path = $location.path().split('/');
var pathSize = path.length;
$scope.events = [];
if(pathSize === 2){
console.log("No event ID");
$scope.events = EventService.query();
}
else{
console.log("Event ID specified");
EventService.get({"eventID": path[pathSize - 1]}, function(data){
//$scope.events.push(data);
console.log(data);
}, function(error){
console.log(error);
});
}
}]);
and the service logic
service.factory('EventService', function($resource){
return $resource('api/viewEvent/:eventID');
});
It never makes it back to the controller so I'm "confident" it's not that. (watch it be that)
Not sure if the best way, but I got it working by doing
In service:
service.factory('EventService', function($resource){
return $resource('api/viewEvent/:eventID',
{eventID:"#eventID"},
{
'getSingleEvent': {
url: "api/viewEvent/:eventID",
method: "GET",
isArray: true
}
}
);
controller
var path = $location.path().split('/');
var pathSize = path.length;
EventService.getSingleEvent({"eventID":path[pathSize - 1]}, function(result){
$scope.updateEvent();
});
Server
routes = require('./routes')
var router = express.Router();
router.get('/api/viewEvent/:eventID', routes.viewEvent);
and in the routes directory I have a js file with
var mongoose = require('mongoose');
var db = mongoose.createConnection('localhost', 'eventApp');
var eventSchema = require('../models/createEvent.js').eventSchema;
var event = db.model('events', eventSchema);
exports.viewEvent = function(req, res){
console.log(req.params.eventID);
if(req.params.eventID) {
event.find({"_id": req.params.eventID}, function (error, events) {
console.log(events);
res.send(events);
});
}
else{
event.find({}, function (error, events) {
console.log(events);
res.send(events);
})
}
};

RESTful CRUD for Angular and Express $resource

I have my JSON querying and creating correctly. I'm a bit stuck on how to remove items from the server. They are being removed in angular but I can't seem to get the connection right for removing them on the server.
My server.js:
var hcController = require('./server/controllers/services-controller.js')
//REST API
app.get('/api/hc', hcController.list);
app.post('/api/hc', hcController.create);
app.delete('/api/hc:_id', hcController.delete);
My server-side model
var mongoose = require('mongoose');
module.exports = mongoose.model('HealingCenterData',{
title: String,
shortname: String,
summary: String,
description: String
});
My server-side controller
var Hc = require('../models/healingcenter-model.js')
module.exports.create = function (req, res) {
var hc = new Hc(req.body);
hc.save(function (err, result){
res.json(result);
});
}
module.exports.list = function (req,res) {
Hc.find({}, function (err, results){
res.json(results);
});
}
module.exports.delete = function (req, res) {
???????
});
}
My angular service:
app.factory("HC", ["$resource", function($resource) {
return {
API: $resource('/api/hc/:id')
}
}]);
My angular controller:
app.controller('servicesController', ['$scope', 'HC','$resource', function ($scope, HC, $resource) {
HC.API.query(function(results) {
$scope.services = results;
});
$scope.createService = function() {
var service = new HC.API();
service.title = $scope.serviceTitle;
service.shortname = $scope.serviceShortname;
service.summary = $scope.serviceSummary;
service.description = $scope.serviceDescription;
service.$save(function(result){
$scope.services.push(result);
$scope.serviceTitle = '';
$scope.serviceShortname = '';
$scope.serviceSummary = '';
$scope.serviceDescription = '';
});
}
$scope.removeItem = function(index){
$scope.services.splice(index, 1);
}
}]);
My JSON structure
{ "_id" : ObjectId("53bea9366a03a66c2dad68bb"), "title" : "Auto Clinic", "shortname" : "auto_clinic", "summary" : "Volunteers evaluate car problems and make minor repairs. Labor is free, and the car owner pays for any needed parts. Oil changes are performed at a reduced cost. All services are performed on Saturdays.", "description" : "No additional information yet.", "__v" : 0 }
On the server side try (I'm assuming you are using moongose) :
exports.delete = function(req,res){
if(req.params.id !==null || req.params.id!==undefined){
Hc.remove({_id:req.params.id},function(err){
res.send(200);
});
}
};
on the client side:
angular controller:
var endPoint = $resource('/api/hc/:id', {id:'#tId'});
$scope.removeItem = function(id){
var ep = new endPoint({tId:id});
ep.$delete(function(res){
//TODO: update local array in scope
});
};
EDIT:
you can just use the resource directly in the controller or just the service as you have done in your case, that's totally fine.

Resources