NodeJS sending response before running the await methods - node.js

My NodeJS controller is sending the response before finish running the wait methods.
The response with the 'OK' message is immediately send back to the client (browser) when I run it. It sends the response before running all the methods.
try {
const ep = await new keysModel().getKeys(); // key is a list of objects
await ep.map( async (key) => {
// some operations with the keys here
// call a fetch to a third party API
await fetch(urlRequest, {
method: 'post',
body: JSON.stringify(myJSONObject),
headers: {
'Authorization': `Basic ${base64.encode(`${key.APIKey}:${key.PasswordKey}`)}`,
'Content-Type': 'application/json'
},
})
.then(async res => res.json())
.then(async json => {
// Here I have some operations with the json response data
// It includes some database queries
})
.catch(async err => {
// await method to send me an email
res.status(501).send('error');
});
});
res.status(202).send('OK');
} catch() {
// await method to send me an email
res.status(501).send('error');
}
Below is the route:
const express = require('express');
const router = express.Router();
router.get('/myPath', myController.testMethod);
I'm new with NodeJS. Trying to understand how it works.
Thanks

I think Promise.all should work, eg. use async await with array map :)
try {
const ep = await new keysModel().getKeys(); // key is a list of objects
await Promise.all(ep.map(async (key) => {
// some operations with the keys here
// call a fetch to a third party API
return fetch(urlRequest, {
method: 'post',
body: JSON.stringify(myJSONObject),
headers: {
'Authorization': `Basic ${base64.encode(`${key.APIKey}:${key.PasswordKey}`)}`,
'Content-Type': 'application/json'
},
})
.then(async res => res.json())
.then(async json => {
// Here I have some operations with the json response data
// It includes some database queries
})
.catch(async err => {
// await method to send me an email
res.status(501).send('error');
});
}));
res.status(202).send('OK');
} catch() {
// await method to send me an email
res.status(501).send('error');
}

By default, map is not async function, using async on map's call back would not be asynced.
you can use one of Promise.all, Promise.allSettled, Promise.any, Promise.race (depends on your needs, please read more about Promises here.
short example for your case, i will use Promise.all for the example.
try {
const ep = await new keysModel().getKeys(); // key is a list of objects
Promise.all(
ep.map((key) => {
// some operations with the keys here
// call a fetch to a third party API
fetch(urlRequest, {
method: 'post',
body: JSON.stringify(myJSONObject),
headers: {
'Authorization': `Basic
${base64.encode(`${key.APIKey}:${key.PasswordKey}`)}`,
'Content-Type': 'application/json'
},
})
.then(/* Handle array of responses */)
.catch(/* Handle errors */);
);
// proceed..
} catch() {
// await method to send me an email
res.status(501).send('error');
}

Related

Node.js Paypal how to send other data alongside the trigger

I am attempting to send data alongside the code for paypal to the node.js client, but I am not sure where to send that data alongside it.
Here is the client side paypal code:
paypal.Buttons({
// Order is created on the server and the order id is returned
createOrder: (data, actions) => {
return fetch("/api/orders", {
method: "post",
// use the "body" param to optionally pass additional order information
// like product ids or amount
})
.then((response) => response.json())
.then((order) => order.id);
},
// Finalize the transaction on the server after payer approval
onApprove: (data, actions) => {
return fetch(`/api/orders/${data.orderID}/capture`, {
method: "post",
})
.then((response) => response.json())
.then((orderData) => {
// Successful capture! For dev/demo purposes:
console.log('Capture result', orderData, JSON.stringify(orderData, null, 2));
const transaction = orderData.purchase_units[0].payments.captures[0];
alert(`Transaction ${transaction.status}: ${transaction.id}\n\nSee console for all available details`);
// When ready to go live, remove the alert and show a success message within this page. For example:
// const element = document.getElementById('paypal-button-container');
// element.innerHTML = '<h3>Thank you for your payment!</h3>';
// Or go to another URL: actions.redirect('thank_you.html');
});
}
}).render('#paypal-button-container');
Here is the server side paypal code:
app.post("/api/orders", async (req, res) => {
const order = await createOrder();
res.json(order);
});
// capture payment & store order information or fullfill order
app.post("/api/orders/:orderID/capture", async (req, res) => {
const { orderID } = req.params;
const captureData = await capturePayment(orderID);
// TODO: store payment information such as the transaction ID
res.json(captureData);
});
//////////////////////
// PayPal API helpers
//////////////////////
// use the orders api to create an order
async function createOrder() {
const accessToken = await generateAccessToken();
const url = `${base}/v2/checkout/orders`;
const response = await fetch(url, {
method: "post",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify({
intent: "CAPTURE",
purchase_units: [
{
amount: {
currency_code: "USD",
value: "100.00",
},
},
],
}),
});
const data = await response.json();
return data;
}
// use the orders api to capture payment for an order
async function capturePayment(orderId) {
const accessToken = await generateAccessToken();
const url = `${base}/v2/checkout/orders/${orderId}/capture`;
const response = await fetch(url, {
method: "post",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
});
const data = await response.json();
return data;
}
The issue is there is no req.body.stuff anywhere.
The goal I am trying to achieve, is is when the payment is made, I want to send the data of like the string "potato", Before the actual payment goes through on node. Because potato is a client side variable that will then when used in server retrieve the value of the amount to be paid in node.
Where/how do I put this so I can do this?
Thanks.
How to do it is literally explained in the comments of that client-side code:
return fetch("/api/orders", {
method: "post",
// use the "body" param to optionally pass additional order information
// like product ids or amount
})

Firebase functions take MINUTES to write in Firestore

I'm building a mobile app and on some actions (like userCreate), I trigger some Firebase functions to perform an API call to a third service, and then write something in the Firestore database.
Everything works in theory, but in practice, the API calls are quite fast (even with cold start scenarios), but the database writes can take several MINUTES to complete (if they do at all, I suspect that sometimes it takes too long and times out).
Since the API call work just fine, and so does the DB write on some occasion, I suspect that this is simply due to very poor async management from me since I know about nothing in JS.
Here are two of many example functions which are concerned by this issue, just to showcase that it happens whereas I'm triggering functions onCreate or on HTTPS.
onCreate function
const functions = require("firebase-functions");
const axios = require('axios')
// The Firebase Admin SDK to access the Firestore.
const admin = require('firebase-admin');
admin.initializeApp();
// Third party service credentials generation during onCreate request
exports.
buildCredentials = functions.auth.user().onCreate((user) => {
// Request to Third party to generate an Client Access Token
axios({
method: "post",
url: "https://api.ThirdParty.com/api/v1/oauth/token",
data: "client_id=xxx&client_secret=yyy&grant_type=client_credentials&scope=all:all",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
})
.then(function (response) {
//handle success
console.log('new user created: ')
console.log(user.id);
console.log(response.data);
// We write a new document in the users collection with the user ID and the Client Access Token
const db = admin.firestore();
const newUser = {
uid: user.uid,
clientAccessToken: response.data.access_token
};
db.collection('users').doc(user.uid).set(newUser)
.catch(function (error) {
//handle error
console.log(error);
});
})
.catch(function (response) {
//handle error
console.log(response);
});
})
HTTPS onCall function
exports.paymentRequest = functions.https.onCall(async (data, context) => {
const clientAccessToken = data.clientAccessToken;
const recipientIban = data.recipientIban;
const recipientName = data.recipientName;
const paymentDescription = data.paymentDescription;
const paymentReference = data.paymentReference;
const productPrice = parseInt(data.productPrice);
const uid = data.uid;
const jsonData = {
"destinations": [
{
"accountNumber": recipientIban,
"type": "iban"
}
],
"amount": productPrice,
"currency": "EUR",
"market": "FR",
"recipientName": recipientName,
"sourceMessage": paymentDescription,
"remittanceInformation": {
"type": "UNSTRUCTURED",
"value": paymentReference
},
"paymentScheme": "SEPA_INSTANT_CREDIT_TRANSFER"
};
(async function(){
response = await axios({
method: "post",
url: "https://api.ThirdParty.com/api/v1/pay",
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: `Bearer ${clientAccessToken}`,},
data: jsonData,
})
// We write a the payment request ID in the user's document
const db = admin.firestore();
const paymentRequestID = response.data.id;
db.collection('users').doc(uid).set({
paymentRequestID: paymentRequestID
}, { merge: true })
.catch(function (error) {
//handle error
console.log(error);
});
console.log(response.data)
return response.data
})()
})
Am I on the right track thinking that this is an async problem?
Or is it a Firebase/Firestore issue?
Thanks
You are not returning the promises returned by the asynchronous methods (axios() and set()), potentially generating some "erratic" behavior of the Cloud Function.
As you will see in the three videos about "JavaScript Promises" from the official Firebase video series you MUST return a Promise or a value in a background triggered Cloud Function, to indicate to the platform that it has completed, and to avoid it is terminated before the asynchronous operations are done or it continues running after the work has been completed.
The following adaptations should do the trick (untested):
onCreate Function:
buildCredentials = functions.auth.user().onCreate((user) => {
// Request to Third party to generate an Client Access Token
return axios({
method: "post",
url: "https://api.ThirdParty.com/api/v1/oauth/token",
data: "client_id=xxx&client_secret=yyy&grant_type=client_credentials&scope=all:all",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
})
.then(function (response) {
//handle success
console.log('new user created: ')
console.log(user.id);
console.log(response.data);
// We write a new document in the users collection with the user ID and the Client Access Token
const db = admin.firestore();
const newUser = {
uid: user.uid,
clientAccessToken: response.data.access_token
};
return db.collection('users').doc(user.uid).set(newUser)
})
.catch(function (response) {
//handle error
console.log(response);
return null;
});
})
Callable Function:
exports.paymentRequest = functions.https.onCall(async (data, context) => {
try {
const clientAccessToken = data.clientAccessToken;
const recipientIban = data.recipientIban;
const recipientName = data.recipientName;
const paymentDescription = data.paymentDescription;
const paymentReference = data.paymentReference;
const productPrice = parseInt(data.productPrice);
const uid = data.uid;
const jsonData = {
// ...
};
const response = await axios({
method: "post",
url: "https://api.ThirdParty.com/api/v1/pay",
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: `Bearer ${clientAccessToken}`,
},
data: jsonData,
})
// We write a the payment request ID in the user's document
const db = admin.firestore();
const paymentRequestID = response.data.id;
await db.collection('users').doc(uid).set({
paymentRequestID: paymentRequestID
}, { merge: true })
console.log(response.data)
return response.data
} catch (error) {
// See https://firebase.google.com/docs/functions/callable#handle_errors
}
})

How do I call a different REST API within a express route?

I have an express.js REST API that I have created with a variety of routes. I'd like to create a route to call another REST API and then return the result. Ideally, it should look something like the following:
router.post('/CreateTicket', cors(corsOptions), function(req, res, next) {
//make a call to another rest api and then res.send the result
}
The REST API route that I am calling is a POST request and will take in a JSON body with the information for the ticket. It then will return a JSON response containing the ticket information and ticket link.
Essentially, I just want to pass req.body as the body of the API call and then res.send() the response of the API call. I was trying to figure out some way to use fetch or requests, but was just getting confused.
Thank you so much for any help that anyone can offer!
I would suggest to use axios if you want to call the third-party API. The simple way of doing is to create an options(config) pass it to the axios object.
npm i axios --save
Axios config
const options = {
'method': 'POST',
'url': 'https://URL',
'headers': {
'Content-Type': 'application/json'
},
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
};
try {
const result = await axios(options);
console.log(result);
} catch (e) {
console.log(e);
}
In your route file:
const axios = require('axios');
const getData = async (body) => {
const options = {
'method': 'POST',
'url': 'https://URL',
'headers': {
'Content-Type': 'application/json'
},
data: {
body
}
};
try {
const result = await axios(options);
console.log(result);
return result;
} catch (e) {
console.log(e);
}
}
router.post('/CreateTicket', cors(corsOptions), async function(req, res, next) {
//make a call to another rest api and then res.send the result
try {
const response = await getData(req.body);
res.send(response);
} catch (e) {
//wrap your error object and send it
}
}
Note: if you want to pass the data to your own created route you can use res.redirect and it will send the response back. You can check the axios details in the link above.
You would have to use something like axios or http (code originates from link):
const https = require('https')
const options = {
hostname: 'example.com',
port: 443,
path: '/todos',
method: 'GET'
}
const req = https.request(options, res => {
console.log(`statusCode: ${res.statusCode}`)
res.on('data', d => {
return d
})
}

export function with promise in nodejs

I am trying to import a function from an external file I have, which contains a promise.
The resolution of the promise is what should be returned, but I get an error message:
The requested module './functions.js' does not provide an export named 'getOkapiToken'
The POST request works fine when I run it directly in the server, but I need it in a separate file as it will be used by several different files in the future.
This is also the first time I'm using promises, so I'm not sure I handled it properly (though I can't get any errors registered until I deal with the first one).
The functions.js file is built as follows:
import post from 'axios';
export function getOkapiToken(url, user, password) {
//Get username and password for API
const auth = {
"username": user,
"password": password
};
//Create headers for POST request
const options = {
method: 'post',
data: auth,
headers: {
'Content-Type': 'application/json',
'Content-Length': auth.length,
'X-Okapi-Tenant': 'diku'
}
};
//Make API post call
post(url+':9130/authn/login', options)
.then(response => {
return(response.headers['x-okapi-token'])
}).catch((err) => {
return(`There was an error 2: ${err}`)
})
}
And I try to import it as follows:
import { getOkapiToken } from './functions3.js'
import settings from './jsons/config.json';
let OkapiKey = await new Promise((resolve,reject) => {
//Call function to make API post call
let keytoken = getOkapiToken(settings.url,settings.userauth,settings.passauth)
console.log(`There was an error: ${keytoken}`)
if (keytoken.length == 201) {
resolve(keytoken)
} else {
reject('There was an error')
}
})
OkapiKey.then((data) => {
console.log(data)
})
.catch((err) => {
console.error('I have an error:'+err.code);
})
There are three ways to handle asynchronous task in Javascript and wit Node.JS
Pass in a Callback to run in the asynchronous code
Use a Promise that will either resolve or reject the promise
Use the async keyword in front of the function and place await in front of the asynchronous code.
With that said I was able to get the code to work by running a simple node server and I modified your code just a bit to get a response back from the server.
index.js
const { getOkapiToken } = require('./functions.js')
const settings = require('./settings.json')
var OkapiKey = getOkapiToken(settings.url,settings.userauth,settings.passauth)
OkapiKey
.then((data) => {
console.log('I have data'+ data.toString())
})
.catch((err) => {
console.error('I have an error:'+err.code);
})
functions.js
const post = require('axios');
const getOkapiToken = (url, user, password) =>
new Promise(function (resolve, reject) {
//Get username and password for API
const auth = {
"username": user,
"password": password
};
//Create headers for POST request
const options = {
method: 'post',
data: auth,
headers: {
'Content-Type': 'application/json',
'Content-Length': auth.length,
'X-Okapi-Tenant': 'diku'
}
};
post('http://localhost:3000/', options)
.then(response => {
resolve(response.data)
// if (response.headers['x-okapi-token']) {
// resolve(response.headers['x-okapi-token']);
// } else {
// reject((error));
// }
}).catch((err) => {
console.error('Response Error:'+err)
})
})
exports.getOkapiToken = getOkapiToken;

How to show the success response from node server on react-redux framework

I am making a demo react-redux app for the basic understanding of redux and its server is made on nodeJS. I have made a simple form which gets submitted and the server response is res.send('FORM SAVED'). In front-end, I make the post request but is not able to see the response that returns, be it the success response.
My server controller that responds when form details are saved.
export const postData = (req, res) => {
let p = new PostData();
p.name = req.body.name;
p.emp_id = req.body.emp_id;
p.age = req.body.age;
p.dept = req.body.dept;
p.phone = req.body.phone;
p.gender = req.body.gender;
p.save(((err) => {
if (err){res.send(`Error in uploading: ${err}`);}
else {res.send('Form saved');}
}));
}
This is my action:-
export const createPost = postData => dispatch => {
fetch(`${Config.address}/post`, {
method: 'POST',
headers:{
'Content-Type': 'application/json'
},
body: JSON.stringify(postData)
})
.then((post) => {
console.log('post:', post);
dispatch({
type: NEW_POST,
payload: post
})
})
}
This is how I call this in component after clicking submit:-
onSubmit = (e) => {
e.preventDefault();
let postData = {
name: this.state.name,
emp_id: this.state.emp_id,
dept: this.state.dept,
gender: this.state.gender,
age: this.state.age,
phone: this.state.phone
}
this.props.createPost(postData);
}
I want to get the response string ('Form saved') but I don't know how to read that. Can anyone help? Thanks in advance
fetch returns a raw response object. To get an expected data you should call a .json() method on raw response object which is returned by fetch, like below:
export const createPost = postData => dispatch => {
fetch(`${Config.address}/post`, {
method: 'POST',
headers:{
'Content-Type': 'application/json'
},
body: JSON.stringify(postData)
})
.then(response => response.json()) // add this line
.then((post) => {
console.log('post:', post); // you should get an object with `Form saved` or something similar to it
dispatch({
type: NEW_POST,
payload: postData // replace it to the input parameter
})
})
}
Using async/await it becomes more readable:
export const createPost = (postData) => async (dispatch) => {
// send postData to server
const rawResponse = await fetch(`${Config.address}/post`, {
method: 'POST',
headers:{
'Content-Type': 'application/json'
},
body: JSON.stringify(postData)
});
// we are done with server but we need one more step
// turn a raw response to readable JS object
const message = await rawResponse.json()
// message from server response
console.log('Message ', message);
// store same object as we sent to server in redux store
dispatch({ type: NEW_POST, payload: postData });
}
Hope it helps

Resources