Promise rejection: Cannot read property 'status' of undefined - javascript

I am very new to nodejs and I am getting the above-mentioned error for the below code. any help is appreciated. thankyou.
PushNotifications.sendMessageToAll(announcement, (err,res) => {
if (err){
return res.status(500).send()
}
res.status(200).send()
}
sendMessageToAll: function (notification, cb) {
payload = {....}
admin.messaging().subscribeToTopic(tokens,topic).then((res) =>{
return admin.messaging().sendToTopic('/topics/NATA', payload)
}).then((res) =>{
console.log('sent', res)
cb(undefined,res)
}).catch((err) =>{
console.log('Subscribed error',err)
cb(err,undefined)
})
}
}

The error you are getting is because the property status doesn’t exist on undefined that is getting passed back on this line:
cb(err,undefined)
The caller expects a response object, because it wants to use the response object to set a HTTP status code and send back a response to the browser:
return res.status(500).send()
The solution is simple: pass res (short for 'response') to the error callback, instead of undefined:
}).catch((err) =>{
console.log('Subscribed error', err)
cb(err, res)
})

You're literally passing undefined to your callback cb which expects the second parameter to be a response object
.catch((err) =>{
console.log('Subscribed error',err)
cb(err,undefined)
})
You need to make your callback be able to handle having no res to set the status.

In the catch block of your promise you call the callback without the res input:
admin.messaging().subscribeToTopic(tokens,topic).then((res) =>{
return admin.messaging().sendToTopic('/topics/NATA', payload)
}).then((res) =>{
console.log('sent', res)
cb(undefined,res)
}).catch((err) =>{
console.log('Subscribed error',err)
cb(err,undefined) // <-- HERE the second param is res!
})
}
}
EDIT
res is not defined in the catch block. A way could be:
let outerRes;
admin.messaging().subscribeToTopic(tokens,topic).then((res) =>{
outerRes = admin.messaging().sendToTopic('/topics/NATA', payload)
return outerRes;
}).then((res) =>{
console.log('sent', res)
cb(undefined,res)
}).catch((err) =>{
console.log('Subscribed error',err)
cb(err,outerRes) // <-- HERE the second param is res!
})
}
}

Related

Google Cloud Function async with multiple fetch requests

I'm new to both GCF and Javascript async and have been struggling with this. I perform a fetch call initially and then pass that response as a parameter to a second function which then also performs a separate fetch call.
During the second function, my empty initialized json gets properties added to it, and when that function completes, I want to notify the exports.helloHttp to then do res.end and terminate.
I've tried chaining an additional empty then() but it doesn't seem to be working.
My code:
var json = {}; // <- gets properties added to it during secondFunction()
exports.helloHttp = (req, res) => {
fetch("firstfetchurl.com",requestOptions)
.then(result => result.json())
.then(response => {
// next take the result and create a new product
return secondFunction(response);
})
.catch(error => console.log('error', error));
// res.end(JSON.stringify(json)); <- this is what I want my cloud function to output, but only after secondFunction completes
};
Here is the code that would do what you want (replace the fetch URLs and set the appropriate options)
const fetch = require('node-fetch');
exports.helloHttp = async (req, res) => {
return fetch("https://jsonplaceholder.typicode.com/users/1/albums") // First fetch
.then(firstFetchResponse => firstFetchResponse.json())
.then(firstFetchResponse => secondFunction(firstFetchResponse)) // Second fetch
.then(secondFunctionResponse => secondFunctionResponse.json())
.then(finalResponse => res.json(finalResponse)) // This line sends your response to the client
.catch(error => { console.error('Error', error); res.status(500).send('Server Error') }); // In case an error, log and send an error response
};
async function secondFunction(data) {
// Logic of your second function. Here just does another fetch using the data from the first request
let firstAlbumId = data[0].id
return fetch(`https://jsonplaceholder.typicode.com/albums/${firstAlbumId}/photos`);
}
The same function can use an await like this
exports.helloHttp = async (req, res) => {
try {
let response = await fetch("https://jsonplaceholder.typicode.com/users/1/albums") // Note the await on this line
.then(result => result.json())
.then(firstFetchResponse => secondFunction(firstFetchResponse))
.then(secondFetchResponse => secondFetchResponse.json());
res.json(response); // Finally you are sending the response here.
} catch (error) {
console.error(error);
res.status(500).send('Server Error');
}
};
Finally you would also need to make sure that the package.json has the dependency for node-fetch
{
"name": "sample-http",
"version": "0.0.1",
"dependencies": {
"node-fetch": "^2.6.0" // This line must be there
}
}
For sending the JSON response, it uses this method.
result.json() is not an asynchronous operation, therefore you don't need to use a then() block. The following should do the trick;
exports.helloHttp = (req, res) => {
fetch("firstfetchurl.com",requestOptions)
.then(result => {
return secondFunction(result.json());
})
.catch(error => console.log('error', error));
//...
Note that, depending on the exact goal of you helloHttp function, you may need to return the entire promises chain, as follows:
exports.helloHttp = (req, res) => {
return fetch("firstfetchurl.com",requestOptions) // Note the return
.then(result => {
return secondFunction(result.json());
})
.catch(error => console.log('error', error));
//...

javascript promise handling, fail to handle error

I'm having some trouble understanding what I'm doing wrong. I have a function that receives a url to which should make a GET request, in case of success should fill a combo with the received data (this depends which function calls it), in case of fail it should execute some common code.
getFirstCombo = () => {
this.getFromApi('/First/GetAll')
.then(data => this.setState({firstComboOptions: this.parseCombo(data)}))
.catch(error => console.log('ERROR2: ', error));
}
getSecondCombo = () => {
this.getFromApi('/Second/GetAll')
.then(data => this.setState({secondComboOptions: this.parseCombo(data)}))
.catch(error => console.log('ERROR2: ', error));
}
parseCombo = (data: any) => {
const combo = data.map(item => (
{ label: item.description, value: item.id }
));
return combo;
}
getFromApi = (url: string) : Promise<any> => {
return restApiAxios.get(url)
.then(response => {
return response.data;
})
.catch(error => {
console.log('ERROR: ', error);
});
}
this code is executed on the componentDidMount of the react component, but when it fails, it first prints :
ERROR: Error: Network Error
at createError (createError.js:16)
at XMLHttpRequest.handleError (xhr.js:83)
and immediately after:
PanelDatos.tsx:50 ERROR2: TypeError: Cannot read property 'map' of undefined
at PanelDatos.parseCombo (PanelDatos.tsx:55)
at PanelDatos.tsx:50
so, when failing executes the catch block from getFromApi and then it tries to execute the then block in getFirstCombo, which triggers the catch block from the same function cause data does not exist, why is that? shouldnt it just execute the first catch?
thanks in advance
.catch returns a promise much like .then, allowing you to return a custom value and handle it that way.
Try doing the following to observe the effect:
Promise
.reject(1)
.catch(e => e) // Catch the error and return it
.then(console.log) // will log 1 to the console
This means you'll need to add some checks if you want to continue to use promises like this:
Promise
.reject(new Error('haha'))
.catch(err => ({err}))
.then(({err, data}) => {
if(err) return // Do nothing
// enter code here
})
However, using async / await will improve readability even more:
getFirstCombo = async () => {
let response
try {
response = await this.getFromApi('/First/GetAll')
} catch (e) {
return // Exit early
}
let parsed
try {
parsed = this.parseCombo(data)
} catch (e) {
console.log(e)
return // Exit early
}
return this.setState({firstComboOptions: parsed})
}
And, of course, throw the error again in your catch block in your api to allow it to handle api calls.
This is happening since inside getFromApi catch method on the error you are not returning anything, so by default, it is returning a resolved promise with null response and the execution goes inside getFirstCombo then method, causing another error. You can update your code to resolve this like:
getFromApi = (url: string): Promise<any> => {
return restApiAxios.get(url)
.then(response => response.data)
.catch(error => Promise.reject(error));
}
The static Promise.reject function returns a Promise that is rejected. So, it will go directly into catch of wherever getFromApi is called.
DEMO:
async function getFromApi(url) {
return fetch(url) // rejects
.then(response => response.json())
.catch(err => Promise.reject(err))
}
async function getFirstCombo() {
getFromApi('https://no-such-server.abcd')
.then(data => console.log('data: ', data))
.catch(error => console.log('ERROR2: ', error));
}
getFirstCombo()
DEMO #2 (With getFirstCombo function not having any catch block) :
async function getFromApi(url) {
return fetch(url) // rejects
.then(response => response.json())
.catch(err => {
console.log('ERROR in getFromApi(): ', err);
return null; // return null, empty array, 0 or false... as per your requirement
})
}
async function getFirstCombo() {
getFromApi('https://no-such-server.abcd')
.then(data => console.log('data: ', data))
// Same value set in catch block of getFromApi will return in this then() block
// Validate this `data` variable before processing it further like:
// if(data === null) this means an error had occurred
// else continue with your logic
}
getFirstCombo()

fetch catch return Unexpected token U in JSON at position 0

i try to throw error message but it throw me this message : (use React and NodeJS)
return SyntaxError: Unexpected token U in JSON at position 0
fetch("http://localhost:3000/api/register", options)
.then(response => response.json())
.then(user => {
alert('Registration successful !')
this.props.history.push("/");
})
.catch(err => {
console.error(err)
console.log(err.message)
});
}
server side :
router.post('/', async (request, response) => {
try {
const user = request.body;
const newUser = await registerLogic.addUser(user);
if (newUser === 0) {
throw "User name already exists";
}
if(newUser === 1){
throw "Something is missing";
}
response.status(201).json(newUser);
} catch (error) {
console.log(error);
response.status(500).send(error);
}
});
The error Unexpected token U in JSON at position 0 tells you pretty much what's going on here: the first character of the JSON you’re trying to parse is literally a “U”.
Looking at your server-side code, you’re throwing an error “User name already exists”, catching that error, and then returning that message as the response body. Unfortunately, it's not valid JSON, hence the error.
Instead try returning a valid JSON response by updating your catch statement like this:
try {
// ...
} catch (error) {
response
.status(500)
.send(JSON.stringify({ error }));
}
Your response is not json, in your catch block return json response.
} catch (error) {
console.log(error);
response.status(500);
response.json({ message: error.message })
}
In your fetch, check status code returned from the backend.
fetch("http://localhost:3000/api/register", options)
.then(response => response.json())
.then(user => {
if (user.status !== 200) {
alert('ERR', user);
return;
}
alert('Registration successful !')
this.props.history.push("/");
})
.catch(err => {
console.error(err)
console.log(err.message)
});
}
The other answers deal with your original issue well. To answer your followup questions
when i do it now it not get the catch and contine to .then and send me success message
Fetch will not reject the promise for failed HTTP status codes. This may be a surprise compared to Angular or jQuery which will. Fetch will only reject the promise for network level errors, connection dropped out etc.
This is stated in the docs
What you can do instead is something like this:
fetch("http://localhost:3000/api/register", options)
.then(async response => {
if (!response.ok)
throw new Error(await response.text());
return response;
})
.then(response => response.json())
.then(user => {
alert('Registration successful !')
this.props.history.push("/");
})
.catch(err => {
console.error(err)
console.log(err.message)
});
It will check for a successful HTTP status code and reject the promise chain with the server response if it was not. You may want to create your own exception extending the Error class rather than throwing it directly.

How to define a function which returns Promise to a Express Route function?

I have a business leve database module called "db_location" which uses the node-fetch module to get some data from a remote server via REST API.
**db_location.js** DB LOGIC
const p_conf = require('../parse_config');
const db_location = {
getLocations: function() {
fetch(`${p_conf.SERVER_URL}/parse` + '/classes/GCUR_LOCATION', { method: 'GET', headers: {
'X-Parse-Application-Id': 'APPLICATION_ID',
'X-Parse-REST-API-Key': 'restAPIKey'
}})
.then( res1 => {
//console.log("res1.json(): " + res1.json());
return res1;
})
.catch((error) => {
console.log(error);
return Promise.reject(new Error(error));
})
}
};
module.exports = db_location
I would need to call this function within a Route function so as to separate database processing from controller.
**locations.js** ROUTE
var path = require('path');
var express = require('express');
var fetch = require('node-fetch');
var router = express.Router();
const db_location = require('../db/db_location');
/* GET route root page. */
router.get('/', function(req, res, next) {
db_location.getLocations()
.then(res1 => res1.json())
.then(json => res.send(json["results"]))
.catch((err) => {
console.log(err);
return next(err);
})
});
When I ran http://localhost:3000/locations, I received the following error.
Cannot read property 'then' of undefined
TypeError: Cannot read property 'then' of undefined
It seems the Promise was empty or something wrong down the Promise chain going from one response object to another? What is a best practise for solving this kind of scenario?
EDIT 1
If I changed the getLocations to return res1.json() (which I think is a non-empty Promise according to the node-fetch documentation):
fetch(`${p_conf.SERVER_URL}/parse` + '/classes/GCUR_LOCATION', { method: 'GET', headers: {
'X-Parse-Application-Id': 'APPLICATION_ID',
'X-Parse-REST-API-Key': 'restAPIKey'
}})
.then( res1 => {
return res1.json(); // Not empty as it can be logged to `Promise Object`
})
.catch((error) => {
console.log(error);
return Promise.reject(new Error(error));
})
And the route code was changed to :
db_location.getLocations()
.then(json => res.send(json["results"]))
.catch((err) => {
console.log(err);
return next(err);
})
The exactly same error was thrown.
You need getLocations to return a Promise. At the moment, it's running a fetch, but that fetch isn't connected to anything else, and getLocations is returning undefined (and of course you can't call .then on uundefined)
Instead, change to:
const db_location = {
getLocations: function() {
return fetch( ...
Also, since you're not doing anything special in the getLocations catch block, you might consider omitting it entirely and let the caller handle it.
Your function doesn't return anything.
If you want to use a promise, you need return it.

Promise not returning any data for fetch return

I´m building an express router that uses mongoose to access the database. My current problem relies on this piece of code:
app.use("/authreset", (req, res) => {
authenticator
.resetPassword(
req.body.username,
req.body.password,
req.body.token,
req.body.type
)
.then((response, error) => {
if (error) throw new Error(error);
console.log('*****************');
console.log(response);
if (!response) {
res.sendStatus(401);
return;
}
})
.catch(error => {
console.log('*****************');
console.log(error);
if (error) throw new Error(error);
});
});
resetPassword uses the following mongoose call:
return UserModel
.findByIdAndUpdate(user.id, data, { new: true })
.exec();
For some reason, my route is being called and the response is fine (checked on console.log(response) inside promise).
My problem is that the response is never sent back to the client, that times out the fetch call.
Why is my promise not returning data?
Uh, you log the response, but you never send it (or at least respond with a status code)?
Your code should look more like
app.use("/authreset", (req, res) => {
authenticator.resetPassword(
req.body.username,
req.body.password,
req.body.token,
req.body.type
).then(response => {
console.log(response);
if (!response) {
return res.sendStatus(401);
} else {
return res.sendStatus(200); // <<<
}
}, error => {
console.log(error);
return res.sendStatus(500);
});
});
Notice that the then callback never gets called with more than one argument, so that error you were checking for cannot happen. In the catch handler, you should never rethrow an error if it doesn't get handled further down. Also I changed .then(…).catch(…) to the more fitting .then(…, …).

Categories

Resources