Basically I have to do a POST request an try to save this data on an API after I get the return of this POST and save this information on a database.
So, I did a call to a promise (saveProcessedData or saveErrorData) inside another promise, works as expected although is trowing out a warning:
Unhandled rejection StatusCodeError: 400
const sendData = async(data, item) => {
let payload = [],
dataProcessed = [];
let options = {
method: data.endpoint.method,
url: api._url + data.endpoint.url,
headers:
{
'Postman-Token': api._postmanToken,
'cache-control': api._cache,
'x-ccasset-language': 'en',
Authorization: data.token
},
body: item,
json: true
};
return new Promise((resolve, reject) => {
api._request(options, async (error, response, body) => {
if(!response.body.errorCode && response.statusCode === 200) {
payload = {
body: response.body,
type: data.req.body.type
}
dataProcessed = await db.saveProcessedData(payload);
} else {
payload = {
body: item,
type: data.req.body.type,
description: response.body.message
}
dataProcessed = await db.saveErrorData(payload);
}
if (error) {
reject(error)
}
resolve(dataProcessed);
});
});
}
How Can I catch this error?
It's better if you don't mix callbacks & promises the way you're doing it. You're getting that error because you're not handling the errors correctly inside api._request callback.
Wrap your code inside the callback in a try/catch, because that's how you handle exceptions in async functions.
new Promise(async resolve => {
await Promise.reject() // Unhandled Promise Rejection
}).catch(console.error) // not catched
Should be:
new Promise(async resolve => {
try {
await Promise.reject()
resolve();
} catch(e) {
reject(e)
}
}).catch(console.error) // catched
In any case, since you're wrapping api._request in a Promise, it would be better to do:
const sendData = async(data, item) => {
// ...
const { error, response, body } = await new Promise((resolve, reject) => {
api._request(options, (error, response, body) => resolve({ error, response, body }))
})
if(!response.body.errorCode && response.statusCode === 200) {
payload = {
body: response.body,
type: data.req.body.type
}
dataProcessed = await db.saveProcessedData(payload);
} else {
payload = {
body: item,
type: data.req.body.type,
description: response.body.message
}
dataProcessed = await db.saveErrorData(payload);
}
return dataProcessed;
}
And attach a .catch handler to .sendData
Aside from that in your code, if error is truthy, you're rejecting and then resolving, nothing wrong will happen in that case, but it's better to use return reject(). A Promise, can't be rejected & resolved.
if (error) {
return reject(error)
}
resolve(dataProcessed);
If you're using request, you can use request-promise or request-promise-native which are Promise wrappers around request package.
Just formatting
const sendData = async (data, item) => {
const options = {
method: data.endpoint.method,
url: api._url + data.endpoint.url,
headers: {
'Postman-Token': api._postmanToken,
'cache-control': api._cache,
'x-ccasset-language': 'en',
Authorization: data.token
},
body: item,
json: true
};
return Promise.resolve()
.then(() =>
new Promise((resolve, reject) => {
api._request(options, (error, response) => {
if (!response.body.errorCode && response.statusCode === 200) {
return resolve(
{
body: response.body,
type: data.req.body.type
}
);
}
return reject({
body: item,
type: data.req.body.type,
description: error || response.body.message
});
})
}))
.then(payload => db.saveProcessedData(payload))
.catch(payload => db.saveErrorData(payload))
}
When you call sendData() you can use Promise.prototype.catch() to catch exceptions that occurred in your promise, including any reject():
sendData(myData, myItem)
.then(result => {
//do stuff with result
}
.catch(error => {
//handle error
};
Related
I am creating a loop to create/update users using functions with built-in promises:
for (const user of usersjson.users) {
let getuser = getUser(url, okapikey, user[fieldMap.externalSystemId],
'externalSystemId'); //Check if user exists on the server
await getuser
.then(async (data) => {
if (data.users.length != 0) { //If user exists in the array
update = updateUser(url, okapikey, createduser, data.users[0].id);//Create update function
promises.push(update); //Store function in array
i++;
} else {
create = createNewUser(url, okapikey, createduser);//Create create function
promises.push(create); //Store function in array
i++;
}
}).catch((err) => {
console.error(err);
});
if (promises.length == 50 || i == usersjson.users.length) {//Run functions in batches of 50
await Promise.allSettled(promises)
.then((responses)=> {
for (const response of responses) { //For each promise response
if (response.status == 'fulfilled') { //If fulfilled
if (response.value.status == 204) {
console.log(`${response.value.status}: User ${response.value.request.path.substring(7)} was updated.`);
} else {
if (response.value.status == 201 && response.value.headers.location) {
console.log(`${response.value.status}: User ${response.value.headers['location']} was created.`);
} else {
console.log(response.value.headers.location);
}
}
} else { //Handle rejections
console.log(`There was an error with the user:${response.value}`);
}
}
}).catch((err)=> {
console.log(err);
});
promises=[]; //Empty Promise array
}
}
async function updateUser(url, token, user, userid)
{
return new Promise((resolve, reject) => {
//Create headers for put request
const options = {
method: "put",
headers: {
'x-okapi-token': token,
'x-okapi-tenant':'tenant',
'Content-type':"application/json"
}
};
//Make API get call
user.id=userid; //Adding the required field ID to the JSON
axios.put(`${url}/users/${userid}`, JSON.stringify(user), options)
.then(response => {
if (response.status == 204) {
resolve(response);
} else {
reject(`Error Code: ${err.response.status}\nError Text: ${err.response.data.errors[0].message}\nError Status: ${err}`);
}
}).catch((err) => {
console.error(`Error Code: ${err.response.status}`);
if (typeof err.response.data == 'string') {
console.error(err.response.data);
reject(`Error Code: ${err.response.status}\nError Text: ${err.response.data.errors[0].message}\nError Status: ${err}`);
} else if (err.response.data.errors[0].message) {
console.error(`Error Text: ${err.response.data.errors[0].message}`);
reject(`Error Code: ${err.response.status}\nError Text: ${err.response.data.errors[0].message}\nError Status: ${err}`);
} else {
reject(`Error Code: ${err.response.status}\nError Text: ${err.response.data.errors[0].message}\nError Status: ${err}`);
}
console.log(err.response);
});
});
};
async function createNewUser (url, token, user) {
return new Promise((resolve, reject) => {
//Create headers for put request
const options = {
headers: {
'X-Okapi-token': token,
'Content-type':"application/json"
}
};
//Make API get call
axios.post(`${url}/users`, JSON.stringify(user), options)
.then(response => {
if (response.status == 201) {
resolve(response);
} else {
reject(`Error Code: ${err.response.status}: ${user.externalSystemId},\nError Text: ${err.response.data.errors[0].message},\nError Status: ${err}`)
}
}).catch((err) => {
console.error(`Error on ${user.externalSystemId}: ${err}`);
if (err.response.data && typeof err.response.data == 'string') {
console.error(err.response.data);
reject(`Error Code: ${err.response.status}: ${user.externalSystemId},\nError Text: ${err.response.data.errors[0].message},\nError Status: ${err}`)
} else if (err.response.data.errors[0].message) {
console.error(`Error Text: ${err.response.data.errors[0].message}`);
reject(`Error Code: ${err.response.status}: ${user.externalSystemId},\nError Text: ${err.response.data.errors[0].message},\nError Status: ${err}`)
} else {
reject(`Error Code: ${err.response.status}: ${user.externalSystemId},\nError Text: ${err.response.data.errors[0].message},\nError Status: ${err}`)
}
});
});
};
const getUsers = (url,user,password) =>
{
return new Promise((resolve, reject) => {
//Create headers for POST request
const options = {
method: 'post',
headers: {
'Authorization': 'Basic '+Buffer.from(`${user}:${password}`).toString('base64')
}
}
//Make API get call
axios.get(url, options)
.then(response => {
resolve(response.data);
}).catch((err) => {
console.error(err);
reject(err);
});
});
};
The code and loop works fine when every promise is fulfilled, but once a promise is rejected, the loop breaks. I get the error message, for example:
Error on XXX: Error: Request failed with status code 422 Error Text:
User with this username already exists
node:internal/process/promises:246
triggerUncaughtException(err, true /* fromPromise */);
^
[UnhandledPromiseRejection: This error originated either by throwing
inside of an async function without a catch block, or by rejecting a
promise which was not handled with .catch(). The promise rejected with
the reason "Error Code: 422: XXX, Error Text: User with this username
already exists, Error Status: Error: Request failed with status code
422".] { }
Looking at the code and error, I believe this comes from the "createNewUser" function.
I'm not sure why the code breaks - I added catches to all the functions, handled rejections, and added catch statements in the main body of the code, but the loop still breaks.
What I need is for the loop to continue as usual even if one function fails (I will later change the log from console.log to an actual log file).
update = updateUser(url, okapikey, createduser, data.users[0].id);//Create update function
promises.push(update); //Store function in array
create = createNewUser(url, okapikey, createduser);//Create create function
promises.push(create); //Store function in array
This is inaccurate. You are not storing functions in that array, you actually call the updateUser/createNewUser functions here and store the resulting promises in the array. Then, your loop goes on to sequentially (because of the await) do more getUser operations before actually calling Promise.allSettled on the promises array. In the meantime, some of the promises might already have been rejected without having any handlers attached to them.
This is basically the same problem as discussed in Waiting for more than one concurrent await operation and Any difference between await Promise.all() and multiple await?.
To fix it, collect actual functions that you can execute later in your array:
let functions = [];
for (const user of usersjson.users) {
i++;
try {
const data = await getUser(url, okapikey, user[fieldMap.externalSystemId], 'externalSystemId');
if (data.users.length != 0) {
functions.push(() =>
// ^^^^^
updateUser(url, okapikey, createduser, data.users[0].id)
); // Create update function and store it in array
} else {
functions.push(() =>
// ^^^^^
createNewUser(url, okapikey, createduser)
); // Create create function and store it in array
}
} catch(err) {
console.error(err);
}
if (functions.length == 50 || i == usersjson.users.length) { // in batches of 50
const promises = functions.map(fn => fn()); // Run functions
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
const responses = await Promise.allSettled(promises);
for (const response of responses) {
if (response.status == 'fulfilled') {
if (response.value.status == 204) {
console.log(`${response.value.status}: User ${response.value.request.path.substring(7)} was updated.`);
} else {
if (response.value.status == 201 && response.value.headers.location) {
console.log(`${response.value.status}: User ${response.value.headers['location']} was created.`);
} else {
console.log(response.value.headers.location);
}
}
} else {
console.log(`There was an error with the user:${response.value}`);
}
}
functions = []; // empty functions array
}
}
(I've tried to avoid awaiting any .then(…) chains)
I was trying out the promise function for the REST API instead of using axios method. so I can wait for the result and if there is any error. can anyone help me change this code to promise in node.js so I can do a fetch using promise method. thank you
this is my code
const email = "xxx#xxxx.com"
function isUserExists(email, kc_accessToken) {
let url = `${path}/users?email=${email}`;
return axios_instance.get(url,
{
headers: {
"content-type": "application/json",
"authorization": `Bearer ${kc_accessToken}`
}
}).then(function (response) {
if (response.data.length > 0) {
return true;
} else {
return false;
}
})
.catch(function (error) {
console.log("some error occured");
});
}
Method call
http.createServer(function Test() {
getAccessToken().then(function (response) {
kc_accessToken = response.data.access_token;
IsUserExists(email, kc_accessToken).then((resp) => {
console.log(resp)
if(resp) {
console.log("Do Not Create")
} else if (!resp) {
console.log("Creat a new User")
}
})
}).catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});;
}).listen(8081);
I think you need something like that :
const email = "xxx#xxxx.com"
const request = require('request');
function isUserExists(email, kc_accessToken) {
let url = `${path}/users?email=${email}`;
return new Promise(function(resolve, reject){
request({
url: url,
headers: {
"content-type": "application/json",
"authorization": `Bearer ${kc_accessToken}`
}
}, function (error, response, body) {
if (error) {
console.log("some error occured");
}
if (response.data.length > 0) {
return resolve();
}
return reject();
});
});
}
I'm new to Node JS and I can't seem to figure out why i'm getting an 'undefined' when using an async function. I've wrapped the async function in a promise and it resolves after an API request has been completed.
let req = (url) => new Promise((resolve, reject) => {
var options = {
'method': 'GET',
'url': url,
'headers': {
'Content-Type': 'application/json'
}
}
request(options, function (error, response) {
if (error) {
reject(error)
} else {
resolve(response.body)
}
})
})
.then((results) => {
//console.log(results) //this shows the results when uncommented!
console.log('request has been completed')
})
.catch((error) => {
console.log(error)
})
Here is the function that triggers the 'req' function and then says 'undefined' when I log the value:
let getAllData = async (url) => {
var endpoint = `${url}?apikey=${settings.api_key}`
let res = await req(endpoint)
console.log('run after request') //successfully runs after request is resolved above
console.log(res) //gives me 'undefined'
}
Here is my output.
request has been completed
run after request
undefined
What am I doing wrong here?
You are using a .then / .catch as part of the function definition - which does not make sense. Those constructs should be used as part of the function execution.
Define your function like this:
let req = (url) => new Promise((resolve, reject) => {
var options = {
'method': 'GET',
'url': url,
'headers': {
'Content-Type': 'application/json'
}
}
request(options, function (error, response) {
if (error) {
reject(error)
} else {
resolve(response.body)
}
})
})
Then, at execution time, you do this:
let getAllData = async (url) => {
var endpoint = `${url}?apikey=${settings.api_key}`
let res = await req(endpoint)
console.log('run after request')
console.log(res) // should give response.body
}
OR you do this:
let getAllData = async (url) => {
return new Promise((resolve, reject) => {
var endpoint = `${url}?apikey=${settings.api_key}`
req(endpoint)
.then(res => {
console.log('run after request')
console.log(res) // should give response.body
resolve(res)
})
.catch(err => {
console.error(err)
reject(err)
})
})
}
Basically, define the function and then... either call it with async/await syntax, or with promise-style .then/.catch syntax.
Return a value in your then or remove the then and catch block and do this instead when you call the promise with await:
let res = await req(endpoint).catch(err => console.log(err));
I have tried on both Async Handlers and Non-Async Handlers ways,
to implement dynamodb putItem method. it returned an invalid response.
Its works for commented setTimeout function.
I tried by promisify the dynomodb putItem method in Non-Async Handler way as mentioned and official documentation https://docs.aws.amazon.com/lambda/latest/dg/nodejs-handler.html. But still no luck.
can some one point me out the issue on code?
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB({
region: 'ap-south-1',
apiVersion: '2012-08-10'
});
exports.lambdaHandler = async function(event) {
const promise = new Promise(function(resolve, reject){
// setTimeout(function(){
// console.log("resolvedx")
// resolve({statusCode: 200, body: "resolvedx"})
// }, 200)
dynamodb.putItem({
TableName: 'CUSTOMER_LIST',
Item: {
'CUSTOMER_ID': { N: '012' },
'CUSTOMER_NAME': { S: 'zzzza' }
}
}
, function(err, data) {
if (err) {
console.log("err", err);
reject(JSON.stringify({statusCode:200, body:err}));
} else {
console.log("success", data);
resolve(JSON.stringify({statusCode:200, body:data}));
}
})
})
return promise
}
as Non-Async Handlers
exports.lambdaHandlerx = (event, context, callback) => {
const dbPromise = () => new Promise((resolve, reject) => {
console.log("inside db promise")
dynamodb.putItem({
TableName: 'CUSTOMER_LIST',
Item: {
'CUSTOMER_ID': { N: '023' },
'CUSTOMER_NAME': { S: 'asdddwa' }
}
}
, function(err, data) {
if (err) {
console.log("err", err);
reject(err);
} else {
console.log("success", data);
resolve(data);
}
})
})
Promise.all([dbPromise()]).then(data => {
console.log("then", data)
callback(null, {
'statusCode': 200,
'body': JSON.stringify({
message: "dataxz"+ JSON.stringify(data)
})
})
}
).catch(e => {
callback(null, {
'statusCode': 200,
'body': JSON.stringify({
message: 'err'
})
})
})
}
Output
Function returned an invalid response (must include one of: body, headers, multiValueHeaders or statusCode in the response object). Response received:
I've been working through the workshops for Netlify functions and have stumbled getting a simple Fetch response to work. When running the sample at:
https://github.com/DavidWells/netlify-functions-workshop/blob/master/lessons-code-complete/use-cases/5-fetching-data/functions/node-fetch/node-fetch.js
const fetch = require('node-fetch')
const API_ENDPOINT = 'https://cat-fact.herokuapp.com/facts'
exports.handler = async (event, context) => { let response
try {
response = await fetch(API_ENDPOINT)
// handle response
} catch (err) {
return {
statusCode: err.statusCode || 500,
body: JSON.stringify({
error: err.message
})
} }
return {
statusCode: 200,
body: JSON.stringify({
data: response
}) } }
but I just get the following response:
{"data":{"size":0,"timeout":0}}
The build process is working fine, and I've tried other end points but all give the same results.
Here is a working version of your original code using node-fetch and the callback.
// import fetch from 'node-fetch';
const fetch = require('node-fetch')
const checkStatus = (res) => {
if (res.ok) { // res.status >= 200 && res.status < 300
return res.json()
} else {
throw new Error(res.statusText);
}
}
exports.handler = async function(event, context, callback) {
try {
const response = await fetch('https://cat-fact.herokuapp.com/facts')
const data = await checkStatus(response)
callback(null, {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
})
} catch (error) {
callback(error)
}
}
Note: Netlify functions are just AWS functions and you can read about the AWS Lambda Function Handler in the AWS docs
For async functions, you return a response, error, or promise to the runtime instead of using callback.
I like to use the method recommended in the docs, since it is supported and save the callback for non-async functions.
Here is a working version returning a promise to the async function instead.
// import fetch from 'node-fetch';
const fetch = require('node-fetch')
exports.handler = async (event, context) => {
return new Promise((resolve, reject) => {
fetch('https://cat-fact.herokuapp.com/facts')
.then(res => {
if (res.ok) { // res.status >= 200 && res.status < 300
return res.json();
} else {
resolve({ statusCode: res.status || 500, body: res.statusText })
};
})
.then(data =>{
const response = {
statusCode: 200,
headers: { 'content-type': 'application/json' },
body: JSON.stringify(data)
}
resolve(response);
})
.catch(err => {
console.log(err)
resolve({ statusCode: err.statusCode || 500, body: err.message })
})
})
}
This is just an example and there are other ways to handle the errors, but this example resolved the response rather than rejecting. It is not tested as a production example, but gives you an idea of the difference.
Here are some examples of Netlify functions.
On the handle response line you’ll want to await the json result:
response = await response.json();
Check the MDN article for Using Fetch for more details.
In the end I switched to the Axios library and it worked first time:
const axios = require('axios')
const API_ENDPOINT = 'https://jsonplaceholder.typicode.com/todos/1'
exports.handler = async (event, context) => {
let response
try {
response = await axios.get(API_ENDPOINT)
} catch (err) {
return {
statusCode: err.statusCode || 500,
body: JSON.stringify({
error: err.message
})
}
}
return {
statusCode: 200,
body: JSON.stringify({
data: response.data
})
}
}