How to handle a promise with a callback with nodejs? - node.js

I'm making an app using the nano npm module with nodejs, one of my async functions is intended to create an object in Cloudant but I'm not pretty sure how to handle a Promise.resolve with a callback which is an important part of the response which is supposed my server has to respond.
I do well with creating the document but the next part is to check if there was an error trying to do it, so if there is an error I'd like my server to return an object with the classic error message.
This is my code:
exports.createMeeting = async (body) => {
var response;
object = {"name": "Billy Batson"}
console.log("-------------")
//Trying to insert the document
response = await Promise.resolve(db.insert(object).then((body, err) => {
//This is the part I'm trying to check if the db.insert did fail
if (err) {
response = {
message: 'Failure',
statusCode: '500',
}
console.log(JSON.stringify(response));
} else {
response = {
message: 'Ok',
statusCode: '201',
}
console.log(JSON.stringify(response));
}
}));
}
console.log("******* ", JSON.stringify(response));
return response;
}
If I try to run this code the output is:
-------------
{"message":"Ok","statusCode":"201"}
******* undefined
The first printed object is because the code reached the part where I assign the response object with the status code 201 but the second part doesn't recognize the value of 'response' and the line "return response;" actually doesn't return it, I've confirmed it with postman (it doesn't get a response).
I think the problem here is that I'm not handling correctly the .then() syntax, I've tried changing to a classic callback with:
response = await Promise.resolve(db.insert(object),(body, err) => {
if (err) {
response = {
message: 'Failure',
statusCode: '500',
}
console.log(JSON.stringify(response));
} else {
response = {
message: 'Ok',
statusCode: '201',
}
console.log(JSON.stringify(response));
}
});
But it prints:
-------------
******* {"ok":true,"id":"502c0f01445f93673b06fbca6e984efe","rev":"1-a66ea199e7f947ef40aae2e724bebbb1"}
Which means the code is not getting into the callback (it's not printing 'failure' or 'ok' objects)
What I'm missing here?:(

nano provides promise-based API when a callback is omitted.
This is not how errors are handled in promises. then callback has 1 parameter, there
will be no err.
It's expected that a promise is rejected in case there's an error. Promise.resolve is redundant when there's already a promise and is always redundant with async..await.
It should be:
try {
const body = await db.insert(object);
response = {
message: 'Ok',
statusCode: '201',
}
} catch (err) {
response = {
message: 'Failure',
statusCode: '500',
}
}

Related

How do I return a promise in a Node.js view?

In my backend, I make an api call to a third party website that returns a promise. I need to pass that promise to an html template, however using JSON.stringify() in the backend and then JSON.parse() in the frontend didn't work. I received this error:
JSON.parse: unexpected character at line 1 column 2 of the JSON data
So I've decided to change my strategy and do a fetch request from the html template to my Node.js server. This is my current code:
async function getItems() {
return await api.Items
...
}
app.get('/getItems', function(req, res){
res.end(Buffer.from(getItems()));
});
The problem I've run into is that I can't return a promise with res.end, even with Buffer:
TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received an instance of Promise
I also tried:
app.get('/getItems', function(req, res){
getItems().then(result=>
{
res.end(result);
}
).catch(err =>
{
res.status(504);
}
)
});
However the connection times out with a 504 status.
How do I make the view return a promise?
Edit: In the second example, where I get the result of the promise, it doesn't time out if I set it to res.end('5').
Edit again:
const items = fetch('127.0.0.1/api');
console.log(items);
for (const item of items) { console.log(item); }
Response { type: "basic", url: "127.0.0.1/api", redirected: false, status: 200, ok: true, statusText: "OK", headers: Headers, body: ReadableStream, bodyUsed: false }
Uncaught (in promise) TypeError: items is not iterable
You have to wait for the promise to resolve and THEN send the response. There are a couple ways to do it:
app.get('/getItems', function(req, res){
getItems().then(result => {
res.json(result);
}).catch(err => {
console.log(err);
res.sendStatus(500);
});
});
This would send your items as JSON which is presumably more useful than sending a buffer.
Or, you can use async/await like this:
app.get('/getItems', async function(req, res){
try {
const result = await getItems();
res.json(result);
} catch(err) {
console.log(err);
res.sendStatus(500);
}
});

How do I pass the detailed error message thrown in the node sails backend to a fetch request while calling only one method of the node res object?

I've been trying to get both the status code and the detailed error message that is passed by the sails model function "findOneOrCreate," which throws the error below when the query object doesn't conform to the model's schema
{
code: 'E_INVALID_NEW_RECORD',
details: 'Missing value for required attribute `first_opened_app_at`. Expected a value, but instead, got: undefined',
modelIdentity: 'device'
},
isOperational: true,
code: 'E_INVALID_NEW_RECORD',
details: 'Missing value for required attribute `first_opened_app_at`. Expected a value, but instead, got: undefined',
modelIdentity: 'device'
}
which is thrown in the first catch block in the sails controller method below:
logDevice: async function (req, res) {
const deviceId = req.params.deviceId
const deviceType = req.params.deviceType
Device.findOrCreate({ device_id: deviceId}, { device_id: deviceId, device_type: deviceType })
.then((err, device, wasCreated) => {
if(wasCreated) {
console.log('Logged a new device: ' + device.device_id + "("+device.device_type+")" );
res.send({ 'wasCreated': wasCreated})
}
else {
console.log('Found an existing device: ' + device.device_id + "("+device.device_type+")");
res.send({ 'wasCreated' : wasCreated })
}
})
// Some other kind of usage / validation error
.catch({ name: 'UsageError' }, err => {
console.log(err)
return res.badRequest(err);
})
// Adaptor error
.catch({ name: 'AdapterError' }, err => {
console.log(err)
res.badRequest(err);
})
// If something completely unexpected happened.
.catch(err => {
res.serverError(err);
});
},
The method that I'm using to pass the err object with the detailed error message back is the badrequest method. I'd like to keep using this method because it accurately describes the actual problem, as the request parameters provided aren't conforming to the model's schema.
But when I pass the err object into badRequest as below:
res.badRequest(err)
I'm not getting the detailed error in JSON format (in the first of the formatted code blocks in this question)
I know that if I do
res.send({err: err})
or something along those lines, I'll get the error message, but then I won't get the status codes and other useful info that I'm getting when I'm calling badRequest which are below:
bodyUsed: false
headers: Headers {map: {…}}
ok: false
status: 400
statusText: ""
type: "default"
url: "http://192.168.1.76:1337/devices/logDevice/949235da12ba6ee5/android"
_bodyBlob: Blob {_data: {…}}
_bodyInit: Blob {_data: {…}}
What's the "right" way or a good way for me to get all of the above info - both the detailed error messages and the info I'm getting with badRequest so that in my front end the response variable has all this info inside it while preferably calling a single method attached to the res object? I know I can just merge the two objects or do something like that but I want the code to be "nice" since I'll have to do something similar in a lot of different places.
fetch(global.BASE_URL+'/devices/logDevice/'+DeviceInfo.getUniqueId()+'/'+Platform.OS, {
method: "get"
})
.then( response => {
if (response.ok) {
return response.json()
}
console.log(response)
throw new Error(response);
})
.then( data => {
console.log(data)
})
.catch( err => {
console.log(err)
})
Thank you.
the first of the formatted code blocks in this question
is not a valid JSON
You need to send a valid JSON to the method:
Like the other built-in custom response modules, the behaviour of this method is customizable.
By default, it works as follows:
The status code of the response is set to 400.
Sails sends any provided error data as JSON. If no data is provided,
a default response body will be sent (the string "Bad Request").
You can make these changes in the catch block, make a valid JSON to pass it to badRequest method.
const error = {
detail: err.details,
code: err.code
}
return res.badRequest(error);

Nodejs response and console

I am creating a NodeJS API using Express, PostgreSQL in which I created a controller function which will check the user from the database.
const checkUser = async (req, res) => {
try {
const user = await pool.query('select * from users where email = $1', [req.body.email]);
if (user.rows.length === 0) throw new Error('Error Found');
return res.json("User Found");
} catch (e) {
//======= Output at console ========
//Error: Error Found
console.log(e);
//======== Response Object Received From API ==========
// {
// "msg": "Error Found",
// "Error": {}
// }
res.status(400).send({ msg: 'Error Found', Error: e });
}
};
Consider the situation that the user is not found in the Database so from try block the control passes to the catch block.
1- I am not able to get why the Error thrown from the try block to catch block sends back an empty object as a response. Also, at the same time using console.log prints the correct output value in the console.
2- I want a fix so that I can send e as a response to the User.
The problem is that Error objects are not that easy to serialize (which is intentional). This...
try {
throw new Error('Catch me if you can');
} catch(e) {
console.log(JSON.stringify(e)); // {}
}
... just logs {}, the same as for empty object, because Error objects don't have enumerable properties. And in your case, you don't send that object directly - but make it a property of another object sent to client.
However, there are several ways out of this. If you always need your client to get all the details of an Error, cast that to string (as toString() is overridden):
try {
throw new Error('Catch me if you can');
} catch(e) {
console.log(JSON.stringify(e.toString())); // "Error: Catch me if you can"
}
... or use Error object properties - name and message - as separate fields of your response:
res.status(400).send({
name: e.name || 'Anonymous Error',
msg: e.message || 'no message provided'
});
As sub-approach, you might consider choosing your status code based on type of error you have (always sending 404 for 'not founds', for example).

Adonis has detected an unhandled promise rejection, which may cause undesired behavior in production

You are using adonisJS in a health check service and are trying to make two requests according to axes in a Service. I get a warning from Adonis "Adonis has detected an unhandled promise rejection, which may
cause undesired behavior in production "and my scheduler that monitors this service every 3 minutes just for. What's wrong with my call and why does adonis complain about it?
My strategy was to make a post for a route without login and right after doing a get for a logged route. I take the request token from the post and play it in the request header, but adonis gives me this warning. What is it?
App/Services/JaiminhoService
try {
await axios.post(Env.get('JAIMINHO_URL'), data).then(response => {
if(response.status === 200) {
try {
await axios.get(`${Env.get('JAIMINHO_URL')}/push/schedule/`, {
headers: { Authorization: `Bearer ${response.data?.token}` }
}).then(response => {
if(response.status === 200) {
return {
status: response.status,
message: response.statusText,
service_name: jaiminho,
date,
}
}
})
} catch (error) {
return 'Error'
}
}
else {
//send mail
}
})
return
} catch (error) {
return {
message: 'Error! Please check Jaiminho service.',
service_name: jaiminho,
date
}
}
Warning: Adonis has detected an unhandled promise rejection, which may
cause undesired behavior in production.
To stop this warning, use catch() on promises or wrap await
calls inside try/catch.
Since you are using async/await, you could avoid nesting the .then() calls.
I recommend you to change your code to something like this and see what happens:
try {
const postResponse = await axios.post(Env.get('JAIMINHO_URL'), data);
if (postResponse.status === 200) {
const getResponse = await axios.get(`${Env.get('JAIMINHO_URL')}/push/schedule/`, {
headers: { Authorization: `Bearer ${postResponse.data?.token}` }});
if (getResponse.status === 200) {
return {
status: response.status,
message: response.statusText,
service_name: jaiminho,
date
};
}
} else {
//send mail
}
} catch (error) {
return {
message: 'Error! Please check Jaiminho service.',
service_name: jaiminho,
date
};
}

return does not stops executing code inside request

i am using request package and it works but after performing request and sending status code to user it continues to execute code even though i use return. here is my code
request(longurl, {method: 'HEAD'}, function(error,response,body){
if(error){
return res.status(409).send({
message: 'URL is not valid'
})
}
})
if(other_condition){
return res.status(409).send({})
}
and it gives me
(node:3040) UnhandledPromiseRejectionWarning: Error: Can't set headers
after they are sent.
The problem is that you are calling return in a callback function. This is making that function return, but the outer function will continue execution. Perhaps your other_condition check should be inside the callback, and then you won't need the return statements.
request(longurl, {method: 'HEAD'}, function(error,response,body){
if(error){
res.status(409).send({
message: 'URL is not valid'
})
} else if(other_condition){
res.status(409).send({})
}
})

Resources