I'm a bit new to all this (including Javascript callbacks and ES6). I'm using NodeJS + Express + MongoDB.
I'm calling an Ajax function to update an item and the success Ajax call is never done.
Here is my Ajax call (called from React)
editBug : function(bug){
console.log('about to edit bug with these values',bug);
$.ajax({
url:'/api/bugs',
method: 'PUT',
data:bug
})
.done((jqxhr) => {
console.log('succcess while editing the bug');
this.setState({successVisible : true});
})
.fail((jqxhr) => {
console.log('error : ' + jqxhr);
})
},
Here is my API function:
app.put('/api/bugs',function(req,res){
//console.log('req',req);
console.log('query string : ',req.query);
console.log('query params : ',req.params);
console.log('query body: ',req.body);
let id = new ObjectID(req.body._id);
req.body._id = new ObjectID(req.body._id);
db.collection('bugs').replaceOne(
{_id:id},
req.body,
function(err,result){
assert.equal(err,null);
console.log('Successfull replace!');
res.status(200);
}
);
});
The Successfull replace! log is correctly shown on the server side.
The about to edit bug with these values is correctly shown on the front side. But the succcess while editing the bug log is not shown on front end and it seems .done call is never executed.
The problem is that you are not sending any response back to the browser on node side. Try the following snippet and you should be good to go
Also, I'd like to point out that you should handle the errors. While updating the bugs if something goes wrong, the best practice would be to inform the browser with the 500 status code indicating that the intended action failed. I've added this aspect in the snipped below
app.put('/api/bugs', function(req, res) {
//console.log('req',req);
console.log('query string : ', req.query);
console.log('query params : ', req.params);
console.log('query body: ', req.body);
let id = new ObjectID(req.body._id);
req.body._id = new ObjectID(req.body._id);
db.collection('bugs').replaceOne({
_id: id
},
req.body,
function(err, result) {
if (err) {
console.log('Failed replace');
res.status(500).end(); // <- We set the response status code and end the request
} else {
assert.equal(err, null);
console.log('Successfull replace!');
res.status(200).end(); // <- We set the response status code and end the request
}
}
);
});
Don't you need to end your response object on the Node.js side?
Try adding res.end(); or any kind of response to your response object.
Also, you can use chrome's (or any other browser's) network tab to actually see how your AJAX requests end up, to see if they hang or finish.
Related
Hello I've gotten two api requests to work individually but I'm having a lot of trouble to get them both working on my node.js app. Is there an easy way to make two requests ? I've tried making two requests putting the data into a variable and then rendering both of them but I run into issues with global variables. Any help would be appreciated.
request(url, function (err, response, body) {
if(err){
res.render('index', {weather: null, error: 'Error, please try again'});
} else {
let weather = JSON.parse(body);
if(weather.main == undefined){
res.render('index', {weather: null, error: 'Error, please try again'});
} else {
if (rain == "0,rain"){
let weatherText = `It's ${weather.main.temp} degrees celsius and with wind speeds of ${weather.wind.speed} mph in ${weather.name} ${weather.sys.country}! & ${weather.weather[0].description}` ;
res.render('index', {weather: weatherText, error: null});
}
else{
let weatherText = `It's ${weather.main.temp} degrees celsius and with wind speeds of ${weather.wind.speed} mph in ${weather.name} ${weather.sys.country}!` ;
res.render('index', {weather: weatherText, error: null});
}
}
}
});
request(url2, function (err, response, body) {
if(err){
res.render('index', {news: null, error: 'Error, please try again'});
} else {
let result = JSON.parse(body);
let news = result.articles[0].title
if(news == undefined){
res.render('index', {news: null, error: 'Error, please try again'});
}
else{
res.render('index', {news:news, error: null});
}
}
})
There is no actual need to use promises, although they do simplify code structure, you can just a easily nest requests:
request(url, function(err, response, body) {
request(url2, function(err2, response2, body2) {
// Both request data is available here
})
})
Note that the variable names used in the callback function can be changed here
Should you have a bunch of requests that can be ran independently, look into promise.all
I would use a ‘promise’ to wait until a response has been received from your first request. Once this has happened, you can trigger your second request, (see ‘then’ on the link below). Once your second request returns a result, you can combine the response from both requests, before using the data.
If request B needs to use data from the response of request A, then request A should be first in the chain. This allows you to adjust your second request based on the response from the first e.g. with IF statements. If the two are completely independent, you can pick either request to go first.
There is a section on this link about chaining promises, which should be relevant to what you’re trying to do: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
Details about error handling with promises and ‘catch’ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch
I'm using Angular2 on the client side and a node-express server as my backend. The node-server works as an API-middleware and also as my authentication service. The user-requests must contain a valid JWT token to perform requests on the node-server.
All of my GET functions and other PUT functions are working properly. I wrote a new one, which just should delete an ID on a third-party API, doesn't.
Furthermore, my node-express server sends custom error messages at some points to the client. This comes to my problem, whenever I run my latest PUT-function, my server responds with "No token provided". This happens when the user isn't logged in on the client side.
As I said, all my other functions working. this.createAuthenticationHeaders(); is necessary to perform valid request on the server side. But it's implemented.
In other words, the authentication gets lost between client and server and I get my own error message: "No token provided".
Appointment-Detail.Component.ts
cancelAppointment() {
this.authService.getProfile().subscribe(profile => {
this.username = profile.user.username; // Set username
this.email = profile.user.email; // Set e-mail
if (profile.user.email) {
this.apiService.cancelUserAppointment(this.id).subscribe(data => {
console.log(this.id);
if (!data.success) {
this.messageClass = 'alert alert-danger'; // Set error bootstrap class
this.message = data.message; // Set error message
} else {
this.messageClass = 'alert alert-success'; // Set success bootstrap class
this.message = data.message; // Set success message
// After two seconds, navigate back to blog page
}
});
}
});
}
API Service
cancelUserAppointment(id) {
this.createAuthenticationHeaders();
console.log('API SERVICE ' + id);
return this.http
.put(this.domain + 'api/appointments/' + id + '/cancel', this.options)
.map(res => res.json());
}
An API Service functions that works
getCertificatesByUser(email) {
this.createAuthenticationHeaders();
return this.http
.get(this.domain + 'api/user/' + email + '/certificates', this.options)
.map(res => res.json());
}
Server route to the third party API
router.put('/appointments/:id/cancel', (req, res) => {
console.log('hi');
var id = req.params.id;
const url = process.env.acuityUri + '/appointments/' + id + '/cancel';
console.log(id);
});
Authentication middleware
router.use((req, res, next) => {
const token = req.headers['authorization']; // Create token found in headers
// Check if token was found in headers
if (!token) {
res.json({
success: false,
message: 'No token provided'
}); // Return error
} else {
// Verify the token is valid
jwt.verify(token, config.secret, (err, decoded) => {
// Check if error is expired or invalid
if (err) {
res.json({
success: false,
message: 'Token invalid: ' + err
}); // Return error for token validation
} else {
req.decoded = decoded; // Create global variable to use in any request beyond
next(); // Exit middleware
}
});
}
});
Without doing too much of a deep dive into your auth headers, I see a pretty glaring issue that I think may be the cause of your troubles.
HTTP REST verbs carry different "intents", the intent we specifically care about in this case is wether or not your request should have a body.
GET requests do not carry a body with them.
PUT requests do carry a body.
Because of this, angular's HttpClient request methods (http.get, http.post, etc.) have different method signatures.
To cut to the chase, http.put's method signature accepts 3 parameters: url, body, and options, whereas http.get's method signature only accepts 2: url and options.
If you look at your example, for http.put you are providing this.httpOptions as the second parameter instead of the third, so Angular is packaging up your options object as the PUT request body. This is why you have a working example and a non-working example; the working example is a GET!
The solution? Simply put something else as the request body in the second parameter and shift this.options down to the third parameter slot. If you don't care what it is, just use the empty object: {}.
So your request should look like this:
return this.http
.put(this.domain + 'api/appointments/' + id + '/cancel', {}, this.options)
At the very least, this should send whatever is in this.options to the server correctly. Now wether what's in this.options is correct or not is another story.
Example PUT call from Angular's docs: https://angular.io/guide/http#making-a-put-request
I'm having trouble getting this to run. It runs just fine if I make the same FB.api call directly from the front end or through "serverless invoke local" and it console.logs my response. But when I deploy this function to a lambda and try to call it, I hit the "console.log("TRY"), get a 502 error, and then nothing after that. No response from the FB.api call, no errors, no info at all. I've tried upping the timeout as well and have brought it up to as much as 15 seconds and still getting no response. Anyone else run into this? Thanks!
export async function main(event, context, callback){
var FB = require('fb');
const data = JSON.parse(event.body)
console.log("DATA: ", data)
const requestString = data.Id + '/accounts'
console.log(requestString)
console.log("ACCESS TOKEN: ", data.accessToken)
const pages = []
try{
console.log("TRY")
await FB.api(requestString, 'get', { access_token: data.accessToken }, function(response){
console.log("RESPONSE: ", response)
callback(null, success(response));
})
}
catch (e){
console.log("CATCH")
console.log(e)
callback(null, failure({ status: false }));
}
}
I'm using the new feature of MongoDB 3.6 watch() in order to send updates of the database from the node server to ajax in the client side.
I created a webservice queried periodically by an ajax call. Between two successive ajax calls, i want the second one to get all the updates that occurred in the mean time. I know that i have to resume the change stream as shown in the official documentation here : https://docs.mongodb.com/manual/changeStreams/#resume-a-change-stream . However i didn't find out how to apply this to my specific needs, by that i mean, in which callback can i process my data and send it to the webservice response ?
Here is a part of my server side code : server.js
const pipeline = [
{
$match : {
"operationType" : "insert"
,
"fullDocument.T" : { "$exists":true}
}
},
{
$project: { "fullDocument.ts": 1,
"fullDocument.T":1}
}
];
function getLiveData(handler){
console.log("in getLiveData");
var liveArray=[];
var resumeToken;
const changeStream = dbObject.collection('status').watch(pipeline);
changeStream.hasNext(function(err, change) {
if (err) return console.log(err);
expect(err).to.equal(null);
expect(change).to.exist;
console.log("in changeStream.hasNext");
changeStream.next(function(err, change) {
if (err) return console.log(err);
expect(err).to.equal(null);
console.log("in changeStream.next");
resumeToken = change._id;
expect(change._id).to.exist;
expect(changeStream.resumeToken).to.exist;
changeStream.close(function(err) {
if (err) return console.log(err);
expect(err).to.equal(null);
console.log("in changeStream.close");
const newChangeStream = dbObject.collection('status').watch({ resumeAfter: resumeToken });
newChangeStream.next(function(err, next) {
if (err) return console.log(err);
expect(err).to.equal(null);
expect(next).to.exist;
console.log("in newChangeStream.next");
//my own code
newChangeStream.on("change", function(change) {
console.log('in change stream, change : ',change);
liveArray.push([change.fullDocument.ts, change.fullDocument.T]);
var response = {
"liveArray" : liveArray
};
console.log("from getLiveData : " , response);
handler(response);
});
//my own code
// Since changeStream has an implicit seession,
// we need to close the changeStream for unit testing purposes
newChangeStream.close();
});
});
});
});
}
webservice part :
app.get("/liveDataRequest", function(req, res){
getLiveData(function(data){
console.log("in handler", data);
res.status(200).send(data);
});
And here is the console log, as we can see, the part where i process my data never gets called :
in getLiveData
in changeStream.hasNext
in changeStream.next
in changeStream.close
in newChangeStream.next
in getLiveData
in changeStream.hasNext
in changeStream.next
in changeStream.close
in newChangeStream.next
I am using the Request npm module to extract data from an api and insert it into fields in my Mongo database. Afghanistan, the first country in the database, is the only document that gets populated with data. Every other country gets skipped.
The following code console logs the "beginning" and the "end" sequentially, and then logs the country name. I get that this is the result of the asynchronous nature of javascript, but I'm not sure how it affects this code.
Shouldn't the console log:
begin
country
end
etc...
Here the code:
MongoClient.connect(url, function(err, db) {
db.collection('countries').find().forEach( function(myDoc) {
console.log('beginning');
var code = myDoc.country.iso2;
var options = {
url: 'https://api.tugroup.com/v1/travelsafe/countries/' + code,
headers: {
['X-Auth-API-Key']: '*******',
['Content-Type']: 'application/x-www-form-urlencoded'
}
}
var callback = function(error, response, body) {
console.log(myDoc.country.name);
if (!error && response.statusCode == 200) {
var info = JSON.parse(body);
db.collection('countries').updateOne(
{ 'country.name' : myDoc.country.name },
{ $set: {
'safety.hasAdvisoryWarning' : info.hasAdvisoryWarning,
'safety.hasRegionalAdvisory' : info.hasRegionalAdvisory,
'safety.advisories' : info.advisories,
'safety.advisoryState' : info.advisoryState,
'safety.advisoryText' : info.advisoryText,
'safety.lawAndCulture' : info.lawAndCulture,
'safety.security' : info.safety}
},
function(err, result) {
console.log(err);
db.close();
});
}
}
request(options, callback);
console.log('end');
});
});
The console logs:
begin
end
begin
end
etc...
Algeria
American Samoa
Andorra
etc...
The problem that only the first one is updated is because you close the database connection (db.close) in the callback of updateOne so that after the first update is done your following connection to mongoDB is closed.
If your log output should be like
beginning
$country
end
then you have to move the logging into the callback of your requests or issue your requests sequentially.